From 2295104b3e0a9cef9c8eed3adf54016ccae8d874 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:31:04 -0400 Subject: [PATCH 01/63] docs: define Icod.Terminal 1.12 placement design --- ...1.12.0-advanced-raster-placement-design.md | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md diff --git a/docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md b/docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md new file mode 100644 index 000000000..49755bccf --- /dev/null +++ b/docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md @@ -0,0 +1,235 @@ +# Icod.Terminal 1.12.0 Advanced Raster Placement Design + +**Release:** `1.12.0` +**Theme:** bounded advanced persistent-raster placement geometry +**Base:** published `Icod.Terminal 1.11.1` +**Stable compatibility floor:** `1.0.0` + +## Goal + +Extend the existing opaque persistent-raster placement model with two narrowly scoped, backend-neutral controls that fit the 1.11 ownership contract without turning `Icod.Terminal` into a scene graph: + +1. pixel-space source rectangles; and +2. signed placement z-order. + +Before adding those public controls, complete the already-approved behavior-preserving table-driven cleanup of `TerminalTermInfoSemanticEvidence` so reviewed TermInfo evidence contracts are represented once rather than duplicated across seeding and exact-implementation classification. + +## Why this is the 1.12 scope + +The 1.11 persistent-raster model already owns: + +- a terminal-resident resource; +- one or more opaque placements; +- current-cursor placement; +- optional cell extents; +- placement replacement through `UpdateAsync(...)`; +- acknowledged create/update operations; +- generation invalidation; +- bounded resource/placement registries; +- child-first cleanup. + +Source rectangles and z-order extend one existing placement without changing its ownership graph. By contrast, relative placement introduces parent/child placement lifetimes, chain-depth limits, cycle rejection, and parent invalidation semantics. Unicode placeholders couple graphics to terminal text layout. Animation introduces a separate frame-lifecycle domain. Those features remain outside 1.12. + +## External protocol evidence + +The reviewed Kitty Graphics placement protocol supports a pixel source rectangle through `x`, `y`, `w`, and `h`, and a signed 32-bit z-index through `z`. Kitty defines clipping by intersection when a source rectangle extends outside the source image. + +`Icod.Terminal` will deliberately expose a narrower semantic contract: a source rectangle must lie entirely inside the source raster. The library therefore rejects invalid rectangles before output rather than inheriting backend-specific clipping behavior. + +The common API continues to expose neither Kitty command keys nor Kitty image/placement identifiers. + +## Public API + +### `TerminalRasterSourceRectangle` + +Add one immutable value type: + +```csharp +public readonly struct TerminalRasterSourceRectangle { + public TerminalRasterSourceRectangle( + int x, + int y, + int width, + int height + ); + + public int X { get; } + public int Y { get; } + public int Width { get; } + public int Height { get; } +} +``` + +Semantics: + +- coordinates and extents are source-raster pixels; +- `X` and `Y` are zero-based and non-negative; +- `Width` and `Height` are positive; +- each scalar remains bounded by `TerminalRasterImage.MaximumDimension`; +- construction validates scalar bounds only; +- resource-aware placement validation additionally requires `X + Width <= source width` and `Y + Height <= source height`; +- no clipping is performed by the public contract. + +The type intentionally adds no backend identity, equality policy, slicing behavior, or raster ownership. + +### `TerminalRasterPlacementOptions` + +Extend the existing options type additively: + +```csharp +public sealed class TerminalRasterPlacementOptions { + public int? Columns { get; set; } + public int? Rows { get; set; } + public TerminalRasterSourceRectangle? SourceRectangle { get; set; } + public int? ZIndex { get; set; } +} +``` + +Semantics: + +- `SourceRectangle == null` means the complete source raster; +- `ZIndex == null` means the backend/default z-order, equivalent to ordinary zero-order placement for the reviewed backend; +- `ZIndex` accepts the complete signed 32-bit `int` range; +- negative z-order is preserved as a meaningful request rather than normalized away; +- `Columns` and `Rows` retain their existing `1..16384` validation and semantics. + +## Placement replacement semantics + +`TerminalRasterPlacement.UpdateAsync(...)` remains a complete placement replacement at the terminal's current cursor position while retaining the private placement identity. + +The supplied options describe the complete replacement state. They are not a partial patch against prior options. Therefore: + +- omitted `Columns`/`Rows` return to backend-derived extents; +- omitted `SourceRectangle` returns to the full source raster; +- omitted `ZIndex` returns to the backend/default z-order. + +The library does not need to retain previous placement options. + +## Resource metadata + +`TerminalPersistentRasterResourceState` will retain the immutable source pixel width and height in addition to existing image-number/image-id/generation state. + +This is metadata only. The release continues to forbid retaining arbitrary source raster bytes after successful upload and does not add replay/re-upload behavior. + +Resource metadata is used only to validate a placement source rectangle before terminal output. + +## Backend encoding + +The reviewed persistent Kitty placement encoder will keep the existing: + +```text +Ga=p,i=,p=,C=1 +``` + +and append semantic options deterministically: + +```text +x= +y= +w= +h= +c= +r= +z= +``` + +Only supplied optional values are emitted. If a source rectangle is present, all four source keys are emitted together. + +No cursor-movement behavior changes: `C=1` remains required for all persistent placement create/update operations. + +## Validation and failure behavior + +Validation occurs before output commitment. + +Invalid public scalar values throw `ArgumentOutOfRangeException` from the value/options validation layer. A source rectangle that is individually valid but exceeds the actual resource dimensions also throws `ArgumentOutOfRangeException` before any placement frame is emitted. + +Existing controlled runtime outcomes remain unchanged: + +- stale generation -> `Unavailable` without stale output; +- terminal `ENOENT` -> invalidate resource certainty and return `Unavailable`; +- other well-formed negative acknowledgement -> `Failed`; +- malformed correlated acknowledgement -> parsing failure under existing bounded query ownership; +- cancellation remains effective before output commitment and does not intentionally truncate committed logical control output. + +## TermInfo semantic-evidence cleanup + +The first 1.12 implementation tranche is behavior-preserving. + +`TerminalTermInfoSemanticEvidence` currently duplicates reviewed conditions between `Seed(...)` and `HasExactImplementation(...)`. Refactor these into immutable table-driven contracts: + +- exact semantic contracts for clipboard write, cursor style, and palette color; +- metadata-backed backend advertisement contracts for focus reporting, bracketed paste, and mouse reporting. + +`Seed(...)` iterates the reviewed tables. `HasExactImplementation(...)` consults the same exact-semantic table. Existing predicate helpers remain focused and independently readable. + +This tranche must not: + +- add new TermInfo capabilities; +- change support states or evidence source; +- add public API; +- add production dependencies; +- ingest TermInfo 1.11 persistent-raster lifecycle dimensions into Terminal's coarse capability model. + +## Capability model + +No new `TerminalCapability` value is added in 1.12 for source rectangles or z-order. + +`PersistentRasterGraphics` remains the coarse live-runtime capability. Once that capability is verified, placement options are accepted by the reviewed persistent backend. The existing TermInfo 1.11 integration bridge remains unchanged because its lifecycle profile describes persistent upload/placement/update/delete capabilities, not this richer geometry vocabulary. + +## Resource ceilings + +Existing 1.11 ceilings remain unchanged: + +```text +maximum live persistent resources 256 per session +maximum live persistent placements 4096 per session +maximum raster dimension 16384 pixels +``` + +1.12 adds no unbounded collections and no new terminal-controlled storage. + +## Security and privacy + +Source rectangles and z-order expose only caller-supplied geometry. They do not expose terminal identities, terminal content, or retained raster bytes. + +All acknowledgement input remains untrusted. Identity correlation establishes transaction ownership, not terminal authenticity. + +No new file, temporary-file, shared-memory, process-hosting, or image-decoding path is introduced. + +## Compatibility + +The release is additive over the stable `1.0.0` floor. + +Existing 1.11 source code using only `Columns` and `Rows` remains valid and retains its existing wire behavior. Existing enum numeric values remain frozen. The public API baseline will advance only for the new source-rectangle type and two new placement-option properties. + +## Explicit exclusions + +Version 1.12.0 does not include: + +- relative placements or parent placement identities; +- Unicode placeholder/virtual placements; +- horizontal/vertical relative offsets; +- parent chains, cycle detection, or placement graph ownership; +- animation or frame lifecycle; +- image-file decoding/transcoding; +- source-raster replay/re-upload caches; +- caller-selected raster backends; +- public Kitty ids or raw graphics command dictionaries; +- scene/window/cell/layout/damage ownership belonging to `Icod.DCurses`; +- PTY/ConPTY process hosting. + +## Qualification + +Every implementation tranche must preserve the normal Staging gates. Final 1.12 qualification requires: + +- `net8.0`, `net9.0`, and `net10.0`; +- Runtime Windows; +- Runtime Linux; +- Runtime macOS; +- focused encoder/placement/lifecycle tests; +- package-only consumer coverage for the new public surface; +- generated XML documentation checks; +- current `Icod.DCurses` downstream acceptance/soak; +- package candidate/public API freeze; +- all package contract shards; +- validated package artifact. From 52bf6d402a0f6794951a9cbfd082c6ec5d90fa8a Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:31:29 -0400 Subject: [PATCH 02/63] docs: add Icod.Terminal 1.12.0 roadmap --- Icod.Terminal-1.12.0-Development-Roadmap.md | 258 ++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 Icod.Terminal-1.12.0-Development-Roadmap.md diff --git a/Icod.Terminal-1.12.0-Development-Roadmap.md b/Icod.Terminal-1.12.0-Development-Roadmap.md new file mode 100644 index 000000000..4a73c0de3 --- /dev/null +++ b/Icod.Terminal-1.12.0-Development-Roadmap.md @@ -0,0 +1,258 @@ +# Icod.Terminal 1.12.0 Development Roadmap + +**Release:** `1.12.0` +**Theme:** bounded advanced persistent-raster placement geometry +**Status:** design approved; implementation starting +**Stable compatibility floor:** `1.0.0` +**Prior release:** published `1.11.1` + +## Release objective + +Version 1.12.0 extends the persistent-raster ownership model introduced in 1.11 with two narrowly bounded placement controls: + +- pixel-space source rectangles; and +- signed z-order. + +The release deliberately does not expand into relative placement graphs, Unicode placeholders, animation, cells/windows/layout, or other scene-graph responsibilities. + +Before the new public surface lands, the release completes the already-approved table-driven cleanup of `TerminalTermInfoSemanticEvidence` so reviewed TermInfo evidence contracts are represented once and behavior remains easier to audit. + +The design authority is: + +[`docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md`](docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md) + +The implementation plan is: + +[`docs/superpowers/plans/2026-09-12-1.12.0-advanced-raster-placement.md`](docs/superpowers/plans/2026-09-12-1.12.0-advanced-raster-placement.md) + +The existing persistent ownership authority remains: + +[`docs/Persistent-Raster-Ownership.md`](docs/Persistent-Raster-Ownership.md) + +## Frozen public direction + +The additive 1.12 public surface is planned as: + +```csharp +public readonly struct TerminalRasterSourceRectangle { + public TerminalRasterSourceRectangle( + int x, + int y, + int width, + int height + ); + + public int X { get; } + public int Y { get; } + public int Width { get; } + public int Height { get; } +} + +public sealed class TerminalRasterPlacementOptions { + public int? Columns { get; set; } + public int? Rows { get; set; } + public TerminalRasterSourceRectangle? SourceRectangle { get; set; } + public int? ZIndex { get; set; } +} +``` + +Source rectangles are expressed in source-image pixels and must fit completely inside the resource. The public contract does not expose backend clipping behavior. + +`ZIndex` accepts the full signed 32-bit `int` range. `null` retains the backend/default placement order. + +`UpdateAsync(...)` remains a complete replacement of the placement at the current cursor position, not a partial patch against prior options. + +## Tranche roadmap + +```text +T120 1.12 architecture/API regret gate + roadmap normalization in progress +T121 table-drive TerminalTermInfoSemanticEvidence planned +T122 source-rectangle public contract + resource-aware validation planned +T123 z-order public contract + validation planned +T124 create/update encoder and acknowledged placement integration planned +T125 lifecycle/cancellation/malformed-response/boundary hardening planned +T126 sample/package-only consumer/XML docs/downstream qualification planned +T127 API freeze/release docs/three-OS/package release closure planned +``` + +## T120 — architecture/API regret gate + +Record the 1.12 contract before production changes. + +Acceptance: + +- published `1.11.1` is the explicit base; +- source rectangle and z-order are the only new placement features approved for this release; +- relative placement, Unicode placeholders, animation, and scene-graph ownership remain excluded; +- no new `TerminalCapability` value is planned; +- no production dependency change is planned; +- stable compatibility floor remains `1.0.0`; +- the current long-range roadmap is normalized from stale 1.11.0 wording to published 1.11.1 and this 1.12 line. + +## T121 — table-driven TermInfo semantic evidence + +Refactor the internal `TerminalTermInfoSemanticEvidence` implementation into reviewed immutable tables while preserving exact behavior. + +The exact semantic contracts remain: + +```text +ClipboardWrite <- extended string Ms +CursorStyle <- extended string Ss +PaletteColor <- can_change_color + initialize_color +``` + +The metadata-backed input backend advertisements remain: + +```text +CsiFocusReporting <- fe + fd + kxIN + kxOUT +CsiBracketedPaste <- BE + BD + PS + PE +CsiMouseReporting <- XM + xm + key_mouse prefix validation +``` + +Acceptance: + +- `Seed(...)` and `HasExactImplementation(...)` share one exact-semantic rule table; +- backend advertisements are represented by one reviewed backend rule table; +- all existing evidence states, subjects, sources, routing outcomes, and validation behavior remain unchanged; +- no public API or package dependency change; +- focused routing tests and full Staging matrix remain green. + +## T122 — source rectangle contract + +Add `TerminalRasterSourceRectangle` and `TerminalRasterPlacementOptions.SourceRectangle`. + +Acceptance: + +- zero-based non-negative `X`/`Y`; +- positive `Width`/`Height`; +- scalar bounds respect `TerminalRasterImage.MaximumDimension`; +- resource state retains immutable source width/height metadata only; +- create/update reject a rectangle extending beyond the actual source resource before output; +- `null` means full source image; +- no raster-pixel cache or replay behavior is introduced; +- focused constructor/options/resource-validation tests cover boundaries and no-output failures. + +## T123 — z-order contract + +Add `TerminalRasterPlacementOptions.ZIndex`. + +Acceptance: + +- nullable signed 32-bit `int` surface; +- full `int.MinValue..int.MaxValue` accepted; +- negative values preserved; +- `null` means backend/default order; +- no new capability enum or backend selector; +- source rectangle and z-order remain orthogonal options. + +## T124 — persistent placement integration + +Extend the existing acknowledged placement create/update path. + +The reviewed backend emits: + +```text +x= +y= +w= +h= +z= +``` + +alongside existing private image/placement identities, `C=1`, and optional cell extents. + +Acceptance: + +- all four source rectangle fields are emitted together; +- `z` uses invariant signed decimal formatting; +- create and update use the same encoder contract; +- existing placement acknowledgement correlation is unchanged; +- update retains private placement identity and current-cursor replacement semantics; +- omitted advanced options preserve 1.11 bytes/behavior. + +## T125 — hardening + +Qualify the new geometry through existing lifecycle and query ownership. + +Acceptance includes: + +- source rectangle exact-edge boundaries; +- invalid rectangle before output; +- min/max z-order; +- create/update with combinations of rectangle, cell extents, and z-order; +- caller cancellation before commitment; +- transport failure after commitment; +- malformed/wrong-identity/negative acknowledgement behavior; +- `ENOENT` invalidation; +- generation invalidation/stale local-only cleanup; +- resource/placement capacity ceilings unchanged; +- repeated replacement/disposal cycles. + +## T126 — consumer and downstream qualification + +Update or add executable sample coverage that demonstrates cropping and layering without exposing backend ids or commands. + +Extend fresh package-only consumer validation for: + +- `TerminalRasterSourceRectangle`; +- `SourceRectangle`; +- `ZIndex`; +- placement create/update on `net8.0`, `net9.0`, `net10.0`. + +Require generated XML documentation and current `Icod.DCurses` package acceptance/soak. + +## T127 — stable release closure + +Before stable release: + +- freeze the final 1.12 public API baseline and fingerprint; +- update README, changelog, package release notes, compatibility/security/architecture/persistent-raster authorities, sample catalog, and curated `docs/releases/1.12.0.md`; +- qualify exact head on Runtime Windows/Linux/macOS; +- pass package candidate/API freeze; +- pass Package Foundation, Presentation, Semantic and hardening, Stable 1.x release line; +- produce validated package artifact; +- leave merge, mainline Release validation, tag, and NuGet publication to the maintainer/release workflow. + +## Compatibility guardrails + +Version 1.12 must preserve: + +- existing public signatures and enum numeric values; +- existing 1.11 behavior when the new placement options are unused; +- one authoritative input/query path; +- opaque terminal resource/placement identities; +- generation-scoped persistent ownership; +- no hidden source-raster retention or replay; +- resource ceiling `256` and placement ceiling `4096`; +- direct-transfer persistent transport only; +- production dependencies `Icod.TermInfo 1.11.0` and `Icod.Timing 1.0.0` unless a separately reviewed requirement changes them. + +## Explicit exclusions + +Version 1.12.0 does not include: + +- relative placements; +- parent image/placement identities; +- relative horizontal/vertical offsets; +- placement chains, cycle detection, or graph lifetime ownership; +- Unicode placeholder/virtual placements; +- animation/frame lifecycle; +- pixel offsets inside terminal cells beyond source cropping; +- caller-selected raster backend; +- public Kitty protocol identities; +- generic raw Kitty command dispatch; +- image-file decoding/transcoding; +- PTY/ConPTY process hosting; +- cells/windows/damage/layout policy belonging to `Icod.DCurses`. + +## Release gate + +```text +approved design + -> T121 behavior-preserving internal cleanup + -> T122/T123 additive public value/options contracts + -> T124 acknowledged backend integration + -> T125 adversarial/lifecycle hardening + -> T126 package/downstream/sample qualification + -> T127 exact-head stable release closure +``` From 50cc54ae780df6b9ef7fc9e6616e05a8692f679d Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:32:24 -0400 Subject: [PATCH 03/63] docs: plan Icod.Terminal 1.12.0 implementation --- ...-09-12-1.12.0-advanced-raster-placement.md | 538 ++++++++++++++++++ 1 file changed, 538 insertions(+) create mode 100644 docs/superpowers/plans/2026-09-12-1.12.0-advanced-raster-placement.md diff --git a/docs/superpowers/plans/2026-09-12-1.12.0-advanced-raster-placement.md b/docs/superpowers/plans/2026-09-12-1.12.0-advanced-raster-placement.md new file mode 100644 index 000000000..0ae9ab07d --- /dev/null +++ b/docs/superpowers/plans/2026-09-12-1.12.0-advanced-raster-placement.md @@ -0,0 +1,538 @@ +# Icod.Terminal 1.12.0 Advanced Raster Placement 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 bounded persistent-raster source rectangles and signed z-order while preserving the 1.11 ownership model, after first table-driving the existing TermInfo semantic-evidence reconciliation with exact behavior parity. + +**Architecture:** Keep the public ownership model unchanged: `TerminalRasterResource` owns opaque `TerminalRasterPlacement` handles, and `TerminalRasterPlacementOptions` remains the sole placement-configuration surface. Add one immutable pixel-space source-rectangle value type and one nullable signed z-order option, retain only source width/height metadata in resource state, and extend the existing acknowledged Kitty placement encoder/transaction. Do not add relative-placement graphs, Unicode placeholders, animation, new capability values, or new production dependencies. + +**Tech Stack:** C# 13; `net8.0`, `net9.0`, `net10.0`; xUnit; existing PowerShell packaging/verification scripts; GitHub Actions Staging matrix. + +**Spec:** `docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md` + +## Global Constraints + +- Stable compatibility floor remains `1.0.0`. +- Production dependencies remain `Icod.TermInfo 1.11.0` and `Icod.Timing 1.0.0` unless separately reviewed. +- Existing public enum numeric values do not change. +- Existing 1.11 placement bytes/behavior remain unchanged when `SourceRectangle` and `ZIndex` are both `null`. +- Source rectangles are zero-based pixel rectangles and must fit completely inside the uploaded source raster; no clipping is part of the public contract. +- `ZIndex` accepts the full signed 32-bit `int` range. +- Persistent resource/placement identities remain private. +- No source-raster byte cache or replay/re-upload behavior is added. +- Existing ceilings remain 256 resources, 4096 placements, and 16384 maximum raster dimension. +- Relative placement, Unicode placeholders, animation, scene/window/cell/layout ownership, PTY/ConPTY hosting, image decoding/transcoding, and raw Kitty dispatch remain excluded. + +--- + +### Task 1: T120 planning closure and development version + +**Files:** +- Modify: `Icod.Terminal-Development-Roadmap.md` +- Modify: `Directory.Build.props` +- Modify: `Icod.Terminal.csproj` + +**Interfaces:** +- Consumes: published `1.11.1` repository state. +- Produces: repository development identity `1.12.0-alpha.1` and current-roadmap handoff to T121. + +- [ ] **Step 1: Normalize the current roadmap** + +Change the current stable release from `1.11.0` to `1.11.1`, record the 1.11.1 TermInfo/Terminal integration patch as published, and replace the conditional 1.12 candidate list with the approved 1.12 source-rectangle/z-order scope and tranche links. + +- [ ] **Step 2: Advance development package identity** + +Set: + +```xml +1.12.0 +alpha.1 +``` + +in `Directory.Build.props`. + +Update `PackageReleaseNotes` in `Icod.Terminal.csproj` to state that 1.12 alpha development begins with behavior-preserving TermInfo evidence cleanup followed by bounded persistent-raster source rectangles and z-order; do not claim stable completion. + +- [ ] **Step 3: Verify metadata** + +Run the repository Staging package candidate path and verify the effective package version is `1.12.0-alpha.1` and production package dependencies remain unchanged. + +- [ ] **Step 4: Commit** + +```text +chore: begin Icod.Terminal 1.12.0 development +``` + +--- + +### Task 2: T121 characterize and table-drive exact TermInfo semantic contracts + +**Files:** +- Modify: `tests/Icod.Terminal.Tests/src/Routing/TerminalTermInfoSemanticEvidenceTests.cs` +- Modify: `src/Routing/TerminalTermInfoSemanticEvidence.cs` + +**Interfaces:** +- Consumes: `TerminalSemanticOperation`, `TerminalProtocolBackend`, `TerminalCapabilityEvidenceLedger`, `TerminalDescription`. +- Produces: one immutable exact-semantic contract table used by both `Seed(...)` and `HasExactImplementation(...)`, plus one immutable backend-advertisement contract table used by `Seed(...)`. + +- [ ] **Step 1: Add characterization coverage before refactoring** + +Extend `TerminalTermInfoSemanticEvidenceTests` with parameterized cases that independently prove the exact semantic rule set: + +```text +ClipboardWrite requires Ms +CursorStyle requires Ss +PaletteColor requires CanChangeColor + InitializeColor +``` + +and the backend rule set: + +```text +CsiFocusReporting requires fe + fd + kxIN + kxOUT +CsiBracketedPaste requires BE + BD + PS + PE +CsiMouseReporting requires XM + xm + KeyMouse with SGR or legacy mouse prefix +``` + +For every rule, include one complete case and at least one missing-input case. Assert evidence subject, `Advertised` state, and `TermInfo` evidence source. + +- [ ] **Step 2: Run focused tests to establish the characterization baseline** + +Run: + +```text +dotnet test tests/Icod.Terminal.Tests/Icod.Terminal.Tests.csproj -c Staging --filter FullyQualifiedName~TerminalTermInfoSemanticEvidenceTests +``` + +Expected: all characterization tests pass against the pre-refactor behavior. Record this as the GREEN baseline for a behavior-preserving refactor. + +- [ ] **Step 3: Replace duplicated exact-semantic logic with immutable contracts** + +Inside `TerminalTermInfoSemanticEvidence`, introduce private immutable rule records similar to: + +```csharp +private readonly record struct ExactSemanticContract( + TerminalSemanticOperation Operation, + Func IsAdvertised +); + +private readonly record struct BackendAdvertisementContract( + TerminalProtocolBackend Backend, + Func IsAdvertised +); +``` + +Create static readonly arrays containing exactly the reviewed rules above. Use static predicate methods so delegates are allocated only during static initialization. + +Refactor `Seed(...)` to iterate the two tables. + +Refactor `HasExactImplementation(...)` to search only the exact-semantic table and invoke the matching predicate; unlisted operations return `false` after the existing `Enum.IsDefined(...)` guard. + +Keep `HasAdvertisedMouseProtocol(...)`, `HasExtendedStringContract(...)`, and `HasExtendedString(...)` as focused helpers unless a smaller extraction is required by compiler/analyzer rules. + +- [ ] **Step 4: Re-run focused routing tests** + +Run the same filtered command. Expected: all tests pass with no changed semantic outcomes. + +- [ ] **Step 5: Run full runtime tests** + +Run the normal Staging solution/runtime validation for all three TFMs. Expected: no evidence, routing, package, or analyzer regression. + +- [ ] **Step 6: Commit** + +```text +refactor: table-drive TermInfo semantic evidence +``` + +--- + +### Task 3: T122 RED/GREEN `TerminalRasterSourceRectangle` + +**Files:** +- Create: `src/Graphics/TerminalRasterSourceRectangle.cs` +- Create or modify: `tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterSourceRectangleTests.cs` +- Modify: `src/Graphics/TerminalRasterPlacementOptions.cs` +- Modify: `src/Graphics/TerminalPersistentRasterResourceState.cs` +- Modify: `src/Session/TerminalSession.PersistentRasterGraphics.cs` + +**Interfaces:** +- Produces: + +```csharp +public readonly struct TerminalRasterSourceRectangle { + public TerminalRasterSourceRectangle( int x, int y, int width, int height ); + public int X { get; } + public int Y { get; } + public int Width { get; } + public int Height { get; } +} +``` + +and: + +```csharp +public TerminalRasterSourceRectangle? SourceRectangle { get; set; } +``` + +on `TerminalRasterPlacementOptions`. + +- [ ] **Step 1: Write RED constructor/property tests** + +Tests must require: + +- `(0, 0, 1, 1)` succeeds; +- `(16383, 16383, 1, 1)` succeeds at scalar bounds; +- negative `x`/`y` throw `ArgumentOutOfRangeException`; +- zero/negative width/height throw; +- any scalar above `TerminalRasterImage.MaximumDimension` throws; +- properties return constructor values. + +Run the focused test project and confirm compile failure because `TerminalRasterSourceRectangle` does not yet exist. + +- [ ] **Step 2: Implement the immutable value type minimally** + +Use a public constructor with validation at entry. `X`/`Y` accept `0..MaximumDimension-1`; `Width`/`Height` accept `1..MaximumDimension`. + +- [ ] **Step 3: Verify the value-type tests GREEN** + +Run the focused tests. Expected: pass. + +- [ ] **Step 4: Write RED options/resource-bound tests** + +Extend placement creation/update tests so a resource created from an image of width `W` and height `H`: + +- accepts rectangle `(0,0,W,H)`; +- accepts a proper interior crop; +- rejects `X + Width > W`; +- rejects `Y + Height > H`; +- performs no placement output on rejection. + +These tests should fail because the options/resource state do not yet carry/validate source metadata. + +- [ ] **Step 5: Store immutable source dimensions in resource state** + +Change the internal constructor to: + +```csharp +internal TerminalPersistentRasterResourceState( + uint imageNumber, + long generation, + int sourceWidth, + int sourceHeight +) +``` + +Add read-only `SourceWidth` and `SourceHeight` properties. Validate both in `1..TerminalRasterImage.MaximumDimension`. + +Update registry/resource reservation plumbing to supply source dimensions at resource creation. Do not store source pixel bytes. + +- [ ] **Step 6: Add resource-aware option validation** + +Keep scalar validation in `TerminalRasterPlacementOptions.Validate()` and add an internal overload/helper accepting source dimensions, for example: + +```csharp +internal void Validate( + int sourceWidth, + int sourceHeight +) +``` + +It must call ordinary option validation and then reject a present rectangle whose right or bottom edge exceeds the source dimensions. Use widened arithmetic so addition cannot overflow before comparison. + +Call resource-aware validation before create/update output commitment. + +- [ ] **Step 7: Verify focused source-rectangle tests GREEN** + +Run source rectangle plus persistent placement creation/lifecycle tests. Expected: pass with invalid rectangles producing no output. + +- [ ] **Step 8: Commit** + +```text +feat: add persistent raster source rectangles +``` + +--- + +### Task 4: T123 RED/GREEN signed z-order option + +**Files:** +- Modify: `src/Graphics/TerminalRasterPlacementOptions.cs` +- Modify: `tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterPlacementCreationTests.cs` +- Modify: `tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterPlacementLifecycleTests.cs` + +**Interfaces:** +- Produces: + +```csharp +public int? ZIndex { get; set; } +``` + +- [ ] **Step 1: Write RED public-surface tests** + +Require `TerminalRasterPlacementOptions.ZIndex` to accept and preserve: + +```text +null +0 +-1 +1 +int.MinValue +int.MaxValue +``` + +Confirm compilation fails before the property exists. + +- [ ] **Step 2: Implement the property** + +Add the nullable `int` property with XML documentation. No range validation beyond the CLR `int` domain. + +- [ ] **Step 3: Verify focused tests GREEN** + +Run placement option/creation tests. Expected: pass. + +- [ ] **Step 4: Commit** + +```text +feat: add persistent raster z-order option +``` + +--- + +### Task 5: T124 RED/GREEN placement encoding and acknowledged integration + +**Files:** +- Modify: `src/Graphics/KittyGraphicsPersistentEncoder.cs` +- Modify: `src/Graphics/KittyGraphicsPersistentPlacementTransaction.cs` +- Modify: `tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentEncoderTests.cs` or the existing equivalent encoder test file +- Modify: `tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterPlacementCreationTests.cs` +- Modify: `tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterPlacementLifecycleTests.cs` + +**Interfaces:** +- Consumes: `TerminalRasterPlacementOptions.SourceRectangle`, `.Columns`, `.Rows`, `.ZIndex`. +- Produces: deterministic persistent placement payloads using `x`, `y`, `w`, `h`, `c`, `r`, `z` while retaining private `i`, `p`, and `C=1`. + +- [ ] **Step 1: Write RED encoder tests for source rectangles** + +Require a placement with rectangle `(2,3,4,5)` to emit all of: + +```text +x=2 +y=3 +w=4 +h=5 +``` + +Require no source keys when `SourceRectangle == null`. + +- [ ] **Step 2: Write RED encoder tests for z-order** + +Require exact invariant signed decimal output for `-1`, `int.MinValue`, `0`, and `int.MaxValue`; require no `z` key when `ZIndex == null`. + +- [ ] **Step 3: Preserve existing byte contract when advanced options are absent** + +Add/retain an assertion that the existing `Columns`/`Rows`-only payload remains byte-for-byte identical to 1.11 behavior, including `C=1`. + +- [ ] **Step 4: Extend the encoder minimally** + +Change the placement encoder to consume the complete `TerminalRasterPlacementOptions?` rather than separate extents if that removes duplicated extraction. Emit keys in one deterministic order: + +```text +Ga=p,i=,p=,C=1[,x=...[,y=...[,w=...[,h=...]]]][,c=...][,r=...][,z=...] +``` + +If a source rectangle exists, emit all four keys together. Use `CultureInfo.InvariantCulture` for signed z-order formatting. + +- [ ] **Step 5: Wire create/update through the same options-aware encoder** + +Keep `KittyGraphicsPersistentPlacementTransaction.WriteCoreAsync(...)` as the shared acknowledged output path. Do not fork separate create/update encoding. + +- [ ] **Step 6: Verify focused encoder and placement tests GREEN** + +Run encoder, placement creation, acknowledgement, and lifecycle tests. Expected: pass. + +- [ ] **Step 7: Commit** + +```text +feat: encode advanced persistent raster placement geometry +``` + +--- + +### Task 6: T125 adversarial, lifecycle, and boundary hardening + +**Files:** +- Modify: `tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterHardeningTests.cs` +- Modify: `tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterPlacementAcknowledgementTests.cs` +- Modify: `tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterPlacementLifecycleTests.cs` +- Modify production files only if a RED test exposes an actual defect. + +**Interfaces:** +- Consumes: completed T122-T124 public/options/encoder behavior. +- Produces: acceptance evidence for boundary and failure semantics. + +- [ ] **Step 1: Add exact-edge and combined-option tests** + +Cover source rectangles ending exactly at source right/bottom edges and combinations of source rectangle + `Columns` + `Rows` + negative/positive z-order. + +- [ ] **Step 2: Add no-output invalid rectangle tests for both create and update** + +Assert validation occurs before placement output and before any new query transaction owns input. + +- [ ] **Step 3: Add z-order extrema through real placement transactions** + +Exercise `int.MinValue` and `int.MaxValue` through create/update and correlated acknowledgement handling. + +- [ ] **Step 4: Re-run existing adversarial acknowledgement coverage** + +Confirm wrong identity, malformed response, duplicate fields, timeout/late response ownership, and correlated `ENOENT` behavior are unchanged with advanced options present. + +- [ ] **Step 5: Exercise generation invalidation and disposal** + +Create placements with advanced geometry, invalidate generation, verify subsequent update returns controlled `Unavailable` without output, and verify stale disposal remains local-only. + +- [ ] **Step 6: Run repeated replacement/cleanup cycles** + +Extend the existing bounded hardening loop so advanced geometry participates without changing the 256-resource/4096-placement ceilings or registry churn behavior. + +- [ ] **Step 7: Commit** + +```text +test: harden advanced persistent raster placement +``` + +--- + +### Task 7: T126 sample, package-only consumer, XML docs, and downstream qualification + +**Files:** +- Modify: `samples/Icod.Terminal.PersistentRaster.Sample/Program.cs` +- Modify: `samples/Icod.Terminal.PersistentRaster.Sample/README.md` if present +- Modify: `samples/README.md` +- Modify: `tools/package-persistent-raster-smoke/Program.cs` +- Modify package verification scripts only if the existing smoke entry point needs new assertions; do not weaken any verifier. + +**Interfaces:** +- Consumes: public 1.12 placement API. +- Produces: backend-neutral executable example and fresh NuGet-only compilation/runtime witness on all supported TFMs. + +- [ ] **Step 1: Extend the persistent-raster sample** + +After creating a resource, demonstrate one placement with a valid source crop and nonzero z-order, then update it with a different crop/z-order. Keep the sample free of Kitty ids, Kitty command keys, terminal-brand tests, and backend selection. + +- [ ] **Step 2: Update sample documentation/catalog** + +Explain source rectangles as source-pixel crops and z-order as relative stacking intent; state that 1.12 still does not own scene layout or relative placement graphs. + +- [ ] **Step 3: Extend the package-only smoke consumer** + +Instantiate: + +```csharp +TerminalRasterSourceRectangle rectangle = new( + 0, + 0, + 1, + 1 +); + +TerminalRasterPlacementOptions options = new() { + SourceRectangle = rectangle, + ZIndex = -1 +}; +``` + +and compile/use the existing placement create/update API through the packed NuGet artifact on `net8.0`, `net9.0`, and `net10.0`. + +- [ ] **Step 4: Verify generated XML docs** + +Ensure the packed XML documentation includes `TerminalRasterSourceRectangle`, its constructor/properties, and the two new option properties on all three TFMs. + +- [ ] **Step 5: Run current `Icod.DCurses` package acceptance/soak** + +No downstream code change should be required. Any failure is a stop/review condition. + +- [ ] **Step 6: Commit** + +```text +docs: demonstrate advanced persistent raster placement +``` + +--- + +### Task 8: T127 API freeze and stable release closure + +**Files:** +- Modify: `Directory.Build.props` +- Modify: `Icod.Terminal.csproj` +- Modify: `README.md` +- Modify: `CHANGELOG.md` +- Modify: `Icod.Terminal-Development-Roadmap.md` +- Modify: `Icod.Terminal-1.12.0-Development-Roadmap.md` +- Modify: `docs/Architecture.md` +- Modify: `docs/Persistent-Raster-Ownership.md` +- Modify: `docs/Security-and-Privacy.md` +- Modify: `docs/Compatibility-and-Versioning.md` +- Create: `docs/releases/1.12.0.md` +- Create/update: `docs/Public-API-Baseline-1.12.md` +- Create/update: `docs/Public-API-Baseline-1.12.sha256` +- Create: final 1.12 release-closure tranche document. + +**Interfaces:** +- Consumes: all accepted T120-T126 behavior. +- Produces: stable `1.12.0` package candidate and frozen API baseline. + +- [ ] **Step 1: Generate and review the normalized public API snapshot** + +Confirm the only intended 1.12 additions relative to 1.11 are: + +```text +TerminalRasterSourceRectangle +TerminalRasterSourceRectangle..ctor(int,int,int,int) +TerminalRasterSourceRectangle.X +TerminalRasterSourceRectangle.Y +TerminalRasterSourceRectangle.Width +TerminalRasterSourceRectangle.Height +TerminalRasterPlacementOptions.SourceRectangle +TerminalRasterPlacementOptions.ZIndex +``` + +Any other public API delta is a stop/review condition. + +- [ ] **Step 2: Freeze the 1.12 baseline** + +Write the reviewed baseline text and SHA256 using the existing baseline-generation tooling. Do not modify historical baselines. + +- [ ] **Step 3: Advance stable package metadata** + +Set `VersionPrefix` to `1.12.0` with empty `VersionSuffix`. Update package release notes to link `docs/releases/1.12.0.md` and `docs/Compatibility-and-Versioning.md`. + +- [ ] **Step 4: Synchronize release-facing documentation** + +Document source cropping, z-order, unchanged ownership ceilings/lifecycle, unchanged dependency graph, and exclusions. Update README installation to `1.12.0` only at this stable-candidate stage. + +- [ ] **Step 5: Run exact-head full Staging qualification** + +Require success for: + +```text +Runtime Windows +Runtime Linux +Runtime macOS +Package candidate / public API freeze +Package Foundation +Package Presentation +Package Semantic and hardening +Package Stable 1.x release line +Validated package artifact +``` + +- [ ] **Step 6: Record exact-head acceptance** + +Record the accepted SHA, workflow/run number, final public API fingerprint, dependency graph, and downstream/package witnesses in the 1.12 closure document and roadmap. + +- [ ] **Step 7: Run one final status-only exact-head matrix** + +After closure-only documentation changes, require the same full matrix again before marking the PR ready for review. + +- [ ] **Step 8: Maintainer handoff** + +Leave merge, mainline Release validation, `v1.12.0` tagging, GitHub Release creation, and NuGet publication to the maintainer/release workflow. From 97b1c94587f869dd8c6ecfa3f3646f0f6229d5e6 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:32:43 -0400 Subject: [PATCH 04/63] docs: record Icod.Terminal 1.12 architecture gate --- ...1.12.0-Architecture-and-API-Regret-Gate.md | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 docs/T120-1.12.0-Architecture-and-API-Regret-Gate.md diff --git a/docs/T120-1.12.0-Architecture-and-API-Regret-Gate.md b/docs/T120-1.12.0-Architecture-and-API-Regret-Gate.md new file mode 100644 index 000000000..30de42ec0 --- /dev/null +++ b/docs/T120-1.12.0-Architecture-and-API-Regret-Gate.md @@ -0,0 +1,100 @@ +# T120 — Icod.Terminal 1.12.0 Architecture and API Regret Gate + +- **Release:** `Icod.Terminal 1.12.0` +- **Tranche:** T120 +- **Status:** approved design; implementation may begin +- **Stable compatibility floor:** `1.0.0` +- **Base release:** published `1.11.1` + +## Accepted scope + +Version 1.12 extends the persistent-raster placement model with exactly two new semantic controls: + +1. pixel-space source rectangles; and +2. signed z-order. + +The release first completes the behavior-preserving table-driven cleanup of `TerminalTermInfoSemanticEvidence` approved at 1.11.1 closure. + +## Public API direction + +The accepted additive shape is: + +```csharp +public readonly struct TerminalRasterSourceRectangle { + public TerminalRasterSourceRectangle( + int x, + int y, + int width, + int height + ); + + public int X { get; } + public int Y { get; } + public int Width { get; } + public int Height { get; } +} + +public sealed class TerminalRasterPlacementOptions { + public int? Columns { get; set; } + public int? Rows { get; set; } + public TerminalRasterSourceRectangle? SourceRectangle { get; set; } + public int? ZIndex { get; set; } +} +``` + +Source rectangles are source-raster pixel coordinates and must fit completely inside the resource. `ZIndex` uses the full signed 32-bit `int` domain. `UpdateAsync(...)` remains complete replacement at the current cursor position while retaining the opaque private placement identity. + +## Ownership boundary + +1.12 keeps the 1.11 ownership model intact: + +```text +TerminalSession + -> persistent resource + -> one or more opaque placements + -> create / replace / dispose +``` + +The release adds no placement-parent graph, no virtual-screen state, and no terminal-layout authority. + +Resource state may retain immutable source width/height metadata for pre-output crop validation. It must not retain source raster bytes after upload or introduce hidden replay/re-upload behavior. + +## Capability boundary + +No new `TerminalCapability` value is added. `PersistentRasterGraphics` remains the coarse live capability for the reviewed persistent backend. + +The `Icod.TermInfo.Inspection 1.11.0` integration established in 1.11.1 remains optional and consumer-owned. 1.12 does not ingest TermInfo lifecycle dimensions into Terminal's capability model and does not add Inspection or Source to the production package graph. + +## Explicit exclusions + +T120 rejects the following from 1.12 scope: + +- relative placements; +- parent image/placement identities; +- placement chains or cycle/depth management; +- Unicode placeholder/virtual placements; +- animation/frame lifecycle; +- caller-selected backends; +- public Kitty ids or raw graphics command dictionaries; +- source-image caches or automatic replay; +- image decoding/transcoding; +- PTY/ConPTY process hosting; +- cells/windows/damage/layout ownership belonging to `Icod.DCurses`. + +## Compatibility guardrails + +- stable compatibility floor remains `1.0.0`; +- existing public enum numerics remain unchanged; +- existing placement behavior remains byte-for-byte compatible when advanced options are unused; +- production dependencies remain `Icod.TermInfo 1.11.0` and `Icod.Timing 1.0.0` unless separately reviewed; +- existing resource/placement ceilings remain `256` and `4096`; +- direct persistent transfer remains the reviewed transport. + +## Authorities + +- `Icod.Terminal-1.12.0-Development-Roadmap.md` +- `docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md` +- `docs/superpowers/plans/2026-09-12-1.12.0-advanced-raster-placement.md` +- `docs/Persistent-Raster-Ownership.md` + +T121 may now begin with the table-driven TermInfo semantic-evidence refactor. New public placement API begins only after that behavior-preserving cleanup is accepted. From 00529557d2e69ebfd9092b5c078b82b7954f3193 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:32:51 -0400 Subject: [PATCH 05/63] chore: preserve plans directory --- docs/superpowers/plans/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/superpowers/plans/.keep diff --git a/docs/superpowers/plans/.keep b/docs/superpowers/plans/.keep new file mode 100644 index 000000000..e69de29bb From 989b904e5e44ecd2af018e036148bd2aa26bbce8 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:33:23 -0400 Subject: [PATCH 06/63] docs: normalize roadmap for Icod.Terminal 1.12 --- Icod.Terminal-Development-Roadmap.md | 109 +++++++++++++++++---------- 1 file changed, 70 insertions(+), 39 deletions(-) diff --git a/Icod.Terminal-Development-Roadmap.md b/Icod.Terminal-Development-Roadmap.md index 26449b413..1149a6aa2 100644 --- a/Icod.Terminal-Development-Roadmap.md +++ b/Icod.Terminal-Development-Roadmap.md @@ -4,8 +4,8 @@ - **Package:** `Icod.Terminal` - **Language:** C# 13 - **Target frameworks:** `net8.0`; `net9.0`; `net10.0` -- **Current stable release:** `1.11.0` -- **Next development line:** `1.12.0` — conditional advanced raster placement/lifecycle work +- **Current stable release:** `1.11.1` +- **Next development line:** `1.12.0` — bounded advanced persistent-raster placement geometry - **Stable compatibility floor:** `1.0.0` ## Purpose @@ -96,7 +96,7 @@ ee705250d19d51df92645e5020f188646dd2dbf38483278e6e57ce6fbbc1e9fb ## Published 1.11 result -Version 1.11 adds a separate persistent raster ownership domain above the existing `TerminalRasterImage` and capability-planning contracts. +Version 1.11 established a separate persistent raster ownership domain above the existing `TerminalRasterImage` and capability-planning contracts. The stable semantic flow is: @@ -113,22 +113,6 @@ PersistentRasterGraphics verification The public model remains opaque. Kitty image ids, image numbers, placement ids, raw APC command dictionaries, and backend selection stay internal. -Stable 1.11 guarantees include: - -- `TerminalCapability.PersistentRasterGraphics = 9` with values `0..8` unchanged; -- explicit separation between ephemeral `RasterGraphics` and persistent ownership; -- acknowledged resource creation before a public handle is returned; -- correlated placement create/update responses through the existing one-reader/query path; -- current-cursor placement with optional `Columns` / `Rows` in `1..16384` and no text-cursor movement; -- 256 live resources and 4096 live placements per session; -- nonzero private collision-safe identities with wraparound handling; -- generation-scoped terminal-resident certainty; -- no hidden source-image retention or automatic replay after invalidation/resume; -- `ENOENT` invalidation when the terminal no longer recognizes a believed-current resource; -- child-first cleanup, locally idempotent disposal, and stale local-only cleanup; -- direct transfer only; no file/temp-file/shared-memory transport; -- no scene-graph, cell/window/layout, z-order, animation, source-rectangle, or PTY ownership. - Final 1.11 public API fingerprint: ```text @@ -141,32 +125,78 @@ Detailed 1.11 authorities: - [`docs/releases/1.11.0.md`](docs/releases/1.11.0.md) - [`docs/Persistent-Raster-Ownership.md`](docs/Persistent-Raster-Ownership.md) - [`docs/Public-API-Baseline-1.11.md`](docs/Public-API-Baseline-1.11.md) -- [`docs/C118-1.11.0-Persistent-Raster-Adversarial-Downstream-and-Package-Qualification.md`](docs/C118-1.11.0-Persistent-Raster-Adversarial-Downstream-and-Package-Qualification.md) -- [`docs/C119-1.11.0-Release-Closure.md`](docs/C119-1.11.0-Release-Closure.md) -## Next development line: 1.12.0 +## Published 1.11.1 integration patch + +Version 1.11.1 kept the 1.11 public API and persistent-raster runtime semantics unchanged while proving the intended loose-coupling integration with `Icod.TermInfo.Inspection 1.11.0`. + +The accepted consumer flow is: + +```text +TermInfo static inspection / classification / planning + -> optional Terminal live verification + -> caller-owned Verified lifecycle evidence + -> TermInfo reclassification / replanning + -> Terminal runtime resource / placement execution +``` + +Inspection remains a consumer/test/sample-only dependency. The production package graph remains `Icod.TermInfo 1.11.0` plus `Icod.Timing 1.0.0`. + +Detailed 1.11.1 authorities: + +- [`Icod.Terminal-1.11.1-Development-Roadmap.md`](Icod.Terminal-1.11.1-Development-Roadmap.md) +- [`docs/releases/1.11.1.md`](docs/releases/1.11.1.md) +- [`docs/T1111-E-1.11.1-Release-Closure.md`](docs/T1111-E-1.11.1-Release-Closure.md) + +## Current development line: 1.12.0 + +Version 1.12 is intentionally bounded to advanced placement geometry that extends the existing opaque placement object without adding a placement graph or virtual-screen ownership. + +Approved additions: + +```text +pixel-space source rectangles +signed z-order +``` + +Before those public additions, T121 performs the behavior-preserving table-driven cleanup of `TerminalTermInfoSemanticEvidence` approved at 1.11.1 closure. + +The 1.12 sequence is: + +```text +T120 architecture/API regret gate + roadmap normalization +T121 table-driven TermInfo semantic evidence +T122 source-rectangle public contract + resource-aware validation +T123 z-order public contract +T124 acknowledged create/update encoder integration +T125 lifecycle/adversarial/boundary hardening +T126 sample/package/downstream qualification +T127 API freeze and stable release closure +``` + +The versioned roadmap is: + +[`Icod.Terminal-1.12.0-Development-Roadmap.md`](Icod.Terminal-1.12.0-Development-Roadmap.md) + +The design authority is: -Advanced raster placement/lifecycle features remain candidates rather than promises. +[`docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md`](docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md) -Potential areas include: +### 1.12 boundaries -- source rectangles; -- z-order; -- Unicode placeholders; -- relative placement; -- richer placement geometry; -- animation or frame lifecycle; -- additional terminal-resident resource operations. +Source rectangles are zero-based source-image pixel rectangles and must fit completely inside the uploaded raster. The library rejects invalid rectangles before output rather than exposing backend-specific clipping behavior. -They should enter the core only when: +Z-order is a nullable signed 32-bit placement property. The existing current-cursor/no-cursor-movement placement semantics remain intact. -1. a concrete downstream requirement exists; -2. ownership/lifecycle semantics can be stated precisely; -3. bounded-resource behavior can be specified and tested; -4. the semantic abstraction is useful beyond raw vendor command exposure, or is deliberately isolated as optional protocol-specific functionality; -5. the feature does not turn `Icod.Terminal` into a virtual-screen or scene-graph library. +The following remain deferred beyond 1.12: -The default position after 1.11 is therefore **measure and justify**, not automatically expand. +- relative placements and parent identities; +- placement chains, cycles, and depth limits; +- Unicode placeholder/virtual placements; +- animation/frame lifecycle; +- scene/window/cell/layout ownership; +- source-image caches/replay; +- caller-selected raster backends and raw Kitty dispatch. ## Parallel evidence tracks @@ -205,7 +235,8 @@ The stable 1.x program preserves these boundaries: event ownership completed in 1.9 -> capability visibility completed in 1.10 -> persistent ownership completed in 1.11 - -> advanced placement only if justified in 1.12+ + -> loose lifecycle planning integration completed in 1.11.1 + -> bounded advanced placement geometry in 1.12 ``` `Icod.DCurses` remains the primary downstream witness for richer presentation needs. It should consume `Icod.Terminal` semantic resource/placement ownership rather than force the terminal layer to absorb virtual-screen or scene-graph responsibilities. From 08a5c51bcc80a222c953b28f39b27d99f5f63a8a Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:33:48 -0400 Subject: [PATCH 07/63] temp --- docs/superpowers/plans/.scratch-ignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/superpowers/plans/.scratch-ignore diff --git a/docs/superpowers/plans/.scratch-ignore b/docs/superpowers/plans/.scratch-ignore new file mode 100644 index 000000000..c1b0730e0 --- /dev/null +++ b/docs/superpowers/plans/.scratch-ignore @@ -0,0 +1 @@ +x \ No newline at end of file From cf03eb677c4c0a0968606f5723f6f75afad4aa9d Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:33:54 -0400 Subject: [PATCH 08/63] temp2 --- docs/superpowers/plans/.scratch-ignore-2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/superpowers/plans/.scratch-ignore-2 diff --git a/docs/superpowers/plans/.scratch-ignore-2 b/docs/superpowers/plans/.scratch-ignore-2 new file mode 100644 index 000000000..c1b0730e0 --- /dev/null +++ b/docs/superpowers/plans/.scratch-ignore-2 @@ -0,0 +1 @@ +x \ No newline at end of file From ffe58b02a38bfc5534789bbc6066cf64b191690b Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:34:02 -0400 Subject: [PATCH 09/63] temp3 --- docs/superpowers/plans/.scratch-ignore-3 | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/superpowers/plans/.scratch-ignore-3 diff --git a/docs/superpowers/plans/.scratch-ignore-3 b/docs/superpowers/plans/.scratch-ignore-3 new file mode 100644 index 000000000..c1b0730e0 --- /dev/null +++ b/docs/superpowers/plans/.scratch-ignore-3 @@ -0,0 +1 @@ +x \ No newline at end of file From 11d1431c550a1c950ac4327281eda2cbd00bdd84 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:34:10 -0400 Subject: [PATCH 10/63] temp4 --- docs/superpowers/plans/.scratch-ignore-4 | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/superpowers/plans/.scratch-ignore-4 diff --git a/docs/superpowers/plans/.scratch-ignore-4 b/docs/superpowers/plans/.scratch-ignore-4 new file mode 100644 index 000000000..c1b0730e0 --- /dev/null +++ b/docs/superpowers/plans/.scratch-ignore-4 @@ -0,0 +1 @@ +x \ No newline at end of file From 416c2258d3250105462af3827207a47ed69e5f25 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:34:14 -0400 Subject: [PATCH 11/63] temp5 --- docs/superpowers/plans/.scratch-ignore-5 | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/superpowers/plans/.scratch-ignore-5 diff --git a/docs/superpowers/plans/.scratch-ignore-5 b/docs/superpowers/plans/.scratch-ignore-5 new file mode 100644 index 000000000..c1b0730e0 --- /dev/null +++ b/docs/superpowers/plans/.scratch-ignore-5 @@ -0,0 +1 @@ +x \ No newline at end of file From f364d9b134524b54e4efc709a4a0af09b349a7b3 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:34:19 -0400 Subject: [PATCH 12/63] temp6 --- docs/superpowers/plans/.scratch-ignore-6 | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/superpowers/plans/.scratch-ignore-6 diff --git a/docs/superpowers/plans/.scratch-ignore-6 b/docs/superpowers/plans/.scratch-ignore-6 new file mode 100644 index 000000000..c1b0730e0 --- /dev/null +++ b/docs/superpowers/plans/.scratch-ignore-6 @@ -0,0 +1 @@ +x \ No newline at end of file From c5530538e1e3ffc57c6aaf69ae33bc3d0716d8a8 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:34:40 -0400 Subject: [PATCH 13/63] chore: remove accidental planning marker --- docs/superpowers/plans/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/superpowers/plans/.keep diff --git a/docs/superpowers/plans/.keep b/docs/superpowers/plans/.keep deleted file mode 100644 index e69de29bb..000000000 From 79503ccea3c6811f5ac079ceaa72e92af6e0f56a Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:34:45 -0400 Subject: [PATCH 14/63] chore: remove accidental planning scratch file --- docs/superpowers/plans/.scratch-ignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/superpowers/plans/.scratch-ignore diff --git a/docs/superpowers/plans/.scratch-ignore b/docs/superpowers/plans/.scratch-ignore deleted file mode 100644 index c1b0730e0..000000000 --- a/docs/superpowers/plans/.scratch-ignore +++ /dev/null @@ -1 +0,0 @@ -x \ No newline at end of file From ffac72b096a7d745ab2c79e7fb1ac1456efd4e5e Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:34:48 -0400 Subject: [PATCH 15/63] chore: remove accidental planning scratch file --- docs/superpowers/plans/.scratch-ignore-2 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/superpowers/plans/.scratch-ignore-2 diff --git a/docs/superpowers/plans/.scratch-ignore-2 b/docs/superpowers/plans/.scratch-ignore-2 deleted file mode 100644 index c1b0730e0..000000000 --- a/docs/superpowers/plans/.scratch-ignore-2 +++ /dev/null @@ -1 +0,0 @@ -x \ No newline at end of file From 766901301ef5aba48186cb4971232d8a13f33e34 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:34:57 -0400 Subject: [PATCH 16/63] chore: remove accidental planning scratch file --- docs/superpowers/plans/.scratch-ignore-3 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/superpowers/plans/.scratch-ignore-3 diff --git a/docs/superpowers/plans/.scratch-ignore-3 b/docs/superpowers/plans/.scratch-ignore-3 deleted file mode 100644 index c1b0730e0..000000000 --- a/docs/superpowers/plans/.scratch-ignore-3 +++ /dev/null @@ -1 +0,0 @@ -x \ No newline at end of file From 86f28eb8d5116bea26991188fe238c16302f09f4 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:35:01 -0400 Subject: [PATCH 17/63] chore: remove accidental planning scratch file --- docs/superpowers/plans/.scratch-ignore-4 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/superpowers/plans/.scratch-ignore-4 diff --git a/docs/superpowers/plans/.scratch-ignore-4 b/docs/superpowers/plans/.scratch-ignore-4 deleted file mode 100644 index c1b0730e0..000000000 --- a/docs/superpowers/plans/.scratch-ignore-4 +++ /dev/null @@ -1 +0,0 @@ -x \ No newline at end of file From 5d79ffb38305056d26f564c13da0349bbd945e9d Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:35:05 -0400 Subject: [PATCH 18/63] chore: remove accidental planning scratch file --- docs/superpowers/plans/.scratch-ignore-5 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/superpowers/plans/.scratch-ignore-5 diff --git a/docs/superpowers/plans/.scratch-ignore-5 b/docs/superpowers/plans/.scratch-ignore-5 deleted file mode 100644 index c1b0730e0..000000000 --- a/docs/superpowers/plans/.scratch-ignore-5 +++ /dev/null @@ -1 +0,0 @@ -x \ No newline at end of file From b4d9039960f5ea8806dfd6753210eb00365e9c96 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:35:11 -0400 Subject: [PATCH 19/63] chore: remove accidental planning scratch file --- docs/superpowers/plans/.scratch-ignore-6 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/superpowers/plans/.scratch-ignore-6 diff --git a/docs/superpowers/plans/.scratch-ignore-6 b/docs/superpowers/plans/.scratch-ignore-6 deleted file mode 100644 index c1b0730e0..000000000 --- a/docs/superpowers/plans/.scratch-ignore-6 +++ /dev/null @@ -1 +0,0 @@ -x \ No newline at end of file From 241b8ea7985248065dc90a69551fab9ced7ae00f Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:35:16 -0400 Subject: [PATCH 20/63] chore: begin Icod.Terminal 1.12.0 development --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5e04cf91d..710e41570 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,8 @@ - 1.11.1 - + 1.12.0 + alpha.1 $(VersionPrefix) $(VersionPrefix)-$(VersionSuffix) $(Version) From 3122ae58b3720177be32cd99e57dd13e068809a5 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:35:33 -0400 Subject: [PATCH 21/63] chore: begin Icod.Terminal 1.12.0 development --- Icod.Terminal.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Icod.Terminal.csproj b/Icod.Terminal.csproj index a582d04fb..52f6bddfd 100644 --- a/Icod.Terminal.csproj +++ b/Icod.Terminal.csproj @@ -1,7 +1,7 @@ @@ -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.11.1 adds the executable TermInfo 1.11 to Terminal 1.11 persistent-raster integration contract through a dedicated three-TFM contract-test project and non-packable sample. It preserves the 1.11.0 public API, persistent-raster runtime semantics, and production dependencies; Icod.TermInfo.Inspection 1.11.0 remains test/sample-only. Full notes: https://github.com/uniblab/Icod.Terminal/blob/main/docs/releases/1.11.1.md ; compatibility: https://github.com/uniblab/Icod.Terminal/blob/main/docs/Compatibility-and-Versioning.md + Icod.Terminal 1.12.0-alpha.1 begins bounded advanced persistent-raster placement development. The line first table-drives existing TermInfo semantic-evidence reconciliation with behavior parity, then adds source rectangles and signed z-order to the opaque placement options surface without relative-placement graphs, Unicode placeholders, animation, new capability values, or new production dependencies. Development roadmap: https://github.com/uniblab/Icod.Terminal/blob/feature/1.12.0-advanced-raster-placement/Icod.Terminal-1.12.0-Development-Roadmap.md README.md icon.png https://github.com/uniblab/Icod.Terminal From dc4c1bad308de35784706574c87dd64bbdfaeb57 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:35:52 -0400 Subject: [PATCH 22/63] fix: restore canonical project license header --- Icod.Terminal.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Icod.Terminal.csproj b/Icod.Terminal.csproj index 52f6bddfd..0820ade9d 100644 --- a/Icod.Terminal.csproj +++ b/Icod.Terminal.csproj @@ -1,7 +1,7 @@ From 34ba14d5b07d3a91c15c904e5b982c5afbfe9a5a Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:36:11 -0400 Subject: [PATCH 23/63] docs: mark T120 accepted --- Icod.Terminal-1.12.0-Development-Roadmap.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Icod.Terminal-1.12.0-Development-Roadmap.md b/Icod.Terminal-1.12.0-Development-Roadmap.md index 4a73c0de3..6fc87b0fa 100644 --- a/Icod.Terminal-1.12.0-Development-Roadmap.md +++ b/Icod.Terminal-1.12.0-Development-Roadmap.md @@ -2,7 +2,7 @@ **Release:** `1.12.0` **Theme:** bounded advanced persistent-raster placement geometry -**Status:** design approved; implementation starting +**Status:** T120 accepted; T121 implementation starting **Stable compatibility floor:** `1.0.0` **Prior release:** published `1.11.1` @@ -65,8 +65,8 @@ Source rectangles are expressed in source-image pixels and must fit completely i ## Tranche roadmap ```text -T120 1.12 architecture/API regret gate + roadmap normalization in progress -T121 table-drive TerminalTermInfoSemanticEvidence planned +T120 1.12 architecture/API regret gate + roadmap normalization accepted +T121 table-drive TerminalTermInfoSemanticEvidence in progress T122 source-rectangle public contract + resource-aware validation planned T123 z-order public contract + validation planned T124 create/update encoder and acknowledged placement integration planned @@ -77,7 +77,7 @@ T127 API freeze/release docs/three-OS/package release closure plan ## T120 — architecture/API regret gate -Record the 1.12 contract before production changes. +Accepted. The design, implementation plan, current-roadmap normalization, and T120 authority record freeze source rectangle + z-order as the complete 1.12 feature scope. Acceptance: @@ -89,6 +89,8 @@ Acceptance: - stable compatibility floor remains `1.0.0`; - the current long-range roadmap is normalized from stale 1.11.0 wording to published 1.11.1 and this 1.12 line. +See [`docs/T120-1.12.0-Architecture-and-API-Regret-Gate.md`](docs/T120-1.12.0-Architecture-and-API-Regret-Gate.md). + ## T121 — table-driven TermInfo semantic evidence Refactor the internal `TerminalTermInfoSemanticEvidence` implementation into reviewed immutable tables while preserving exact behavior. From 0adcfd5e536e426b4b695db5349a20138a85b5b8 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:37:34 -0400 Subject: [PATCH 24/63] test: characterize TermInfo semantic evidence rules --- .../TerminalTermInfoSemanticEvidenceTests.cs | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/tests/Icod.Terminal.Tests/src/Routing/TerminalTermInfoSemanticEvidenceTests.cs b/tests/Icod.Terminal.Tests/src/Routing/TerminalTermInfoSemanticEvidenceTests.cs index 76a1f6fc5..958f9dc85 100644 --- a/tests/Icod.Terminal.Tests/src/Routing/TerminalTermInfoSemanticEvidenceTests.cs +++ b/tests/Icod.Terminal.Tests/src/Routing/TerminalTermInfoSemanticEvidenceTests.cs @@ -51,6 +51,71 @@ public void ExactTermInfoRecipesResolveThroughTermInfoBackend() { ); } + [Fact] + public void ExactSemanticRecipesRequireCompleteMetadata() { + TerminalDescription clipboardOnly = new TerminalDescriptionBuilder( "clipboard-only" ) + .SetExtendedString( "Ms", "clipboard" ) + .Build(); + TerminalDescription cursorOnly = new TerminalDescriptionBuilder( "cursor-only" ) + .SetExtendedString( "Ss", "cursor-style" ) + .Build(); + TerminalDescription paletteBooleanOnly = new TerminalDescriptionBuilder( "palette-boolean-only" ) + .SetBoolean( BooleanCapability.CanChangeColor ) + .Build(); + TerminalDescription paletteStringOnly = new TerminalDescriptionBuilder( "palette-string-only" ) + .SetString( + StringCapability.InitializeColor, + "palette" + ) + .Build(); + + Assert.True( + TerminalTermInfoSemanticEvidence.HasExactImplementation( + clipboardOnly, + TerminalSemanticOperation.ClipboardWrite + ) + ); + Assert.False( + TerminalTermInfoSemanticEvidence.HasExactImplementation( + clipboardOnly, + TerminalSemanticOperation.CursorStyle + ) + ); + Assert.True( + TerminalTermInfoSemanticEvidence.HasExactImplementation( + cursorOnly, + TerminalSemanticOperation.CursorStyle + ) + ); + Assert.False( + TerminalTermInfoSemanticEvidence.HasExactImplementation( + cursorOnly, + TerminalSemanticOperation.ClipboardWrite + ) + ); + Assert.False( + TerminalTermInfoSemanticEvidence.HasExactImplementation( + paletteBooleanOnly, + TerminalSemanticOperation.PaletteColor + ) + ); + Assert.False( + TerminalTermInfoSemanticEvidence.HasExactImplementation( + paletteStringOnly, + TerminalSemanticOperation.PaletteColor + ) + ); + + AssertUnknownSemantic( + TerminalSemanticOperation.PaletteColor, + Seed( paletteBooleanOnly ) + ); + AssertUnknownSemantic( + TerminalSemanticOperation.PaletteColor, + Seed( paletteStringOnly ) + ); + } + [Fact] public void TermInfoMetadataAdvertisesExistingInputProtocolBackends() { TerminalCapabilityEvidenceLedger evidence = new(); @@ -76,6 +141,35 @@ public void TermInfoMetadataAdvertisesExistingInputProtocolBackends() { ); } + [Fact] + public void LegacyMousePrefixAdvertisesMouseBackend() { + TerminalDescription terminal = new TerminalDescriptionBuilder( "legacy-mouse" ) + .SetExtendedString( "XM", "mouse-mode" ) + .SetExtendedString( "xm", "mouse-event" ) + .SetString( StringCapability.KeyMouse, "\u001b[M" ) + .Build(); + + AssertAdvertisedBackend( + TerminalSemanticOperation.MouseReporting, + TerminalProtocolBackend.CsiMouseReporting, + Seed( terminal ) + ); + } + + [Fact] + public void UnrecognizedMousePrefixDoesNotAdvertiseMouseBackend() { + TerminalDescription terminal = new TerminalDescriptionBuilder( "unknown-mouse" ) + .SetExtendedString( "XM", "mouse-mode" ) + .SetExtendedString( "xm", "mouse-event" ) + .SetString( StringCapability.KeyMouse, "\u001b[?1000h" ) + .Build(); + + AssertUnknownBackend( + TerminalProtocolBackend.CsiMouseReporting, + Seed( terminal ) + ); + } + [Fact] public void PartialInputMetadataDoesNotBecomeCapabilityEvidence() { TerminalDescription terminal = new TerminalDescriptionBuilder( "partial-input" ) @@ -182,6 +276,19 @@ public void ExactImplementationClassifierExcludesMetadataBackedCsiContracts() { ); } + private static TerminalCapabilityEvidenceLedger Seed( + TerminalDescription terminal + ) { + ArgumentNullException.ThrowIfNull( terminal ); + + TerminalCapabilityEvidenceLedger evidence = new(); + TerminalTermInfoSemanticEvidence.Seed( + terminal, + evidence + ); + return evidence; + } + private static TerminalDescription CreateCompleteTerminal() { return new TerminalDescriptionBuilder( "n158-complete" ) .SetExtendedString( "Ms", "\u001b]52;%p1%s;%p2%s\u001b\\" ) @@ -241,6 +348,17 @@ TerminalCapabilityEvidenceLedger evidence Assert.Equal( TerminalBackendSelectionReason.Advertised, resolution.SelectionReason ); } + private static void AssertUnknownSemantic( + TerminalSemanticOperation operation, + TerminalCapabilityEvidenceLedger evidence + ) { + TerminalCapabilityResolution resolution = evidence.Resolve( + TerminalCapabilitySubject.ForSemanticOperation( operation ) + ); + + Assert.Equal( TerminalCapabilitySupportState.Unknown, resolution.State ); + } + private static void AssertUnknownBackend( TerminalProtocolBackend backend, TerminalCapabilityEvidenceLedger evidence From a7849ebd8fd709559863588f3d1cb2eb68f2de5f Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:39:59 -0400 Subject: [PATCH 25/63] refactor: table-drive TermInfo semantic evidence --- .../TerminalTermInfoSemanticEvidence.cs | 198 +++++++++++------- 1 file changed, 121 insertions(+), 77 deletions(-) diff --git a/src/Routing/TerminalTermInfoSemanticEvidence.cs b/src/Routing/TerminalTermInfoSemanticEvidence.cs index e3fd04ab5..7dfe3b354 100644 --- a/src/Routing/TerminalTermInfoSemanticEvidence.cs +++ b/src/Routing/TerminalTermInfoSemanticEvidence.cs @@ -42,6 +42,36 @@ internal static class TerminalTermInfoSemanticEvidence { private const string SgrMousePrefix = "\u001b[<"; private const string LegacyMousePrefix = "\u001b[M"; + private static readonly ExactSemanticContract[] ExactSemanticContracts = [ + new( + TerminalSemanticOperation.ClipboardWrite, + HasClipboardWrite + ), + new( + TerminalSemanticOperation.CursorStyle, + HasCursorStyle + ), + new( + TerminalSemanticOperation.PaletteColor, + HasPaletteColor + ) + ]; + + private static readonly BackendAdvertisementContract[] BackendAdvertisementContracts = [ + new( + TerminalProtocolBackend.CsiFocusReporting, + HasFocusReporting + ), + new( + TerminalProtocolBackend.CsiBracketedPaste, + HasBracketedPaste + ), + new( + TerminalProtocolBackend.CsiMouseReporting, + HasAdvertisedMouseProtocol + ) + ]; + /// /// Seeds static TermInfo evidence for exact semantic equivalents and reviewed /// metadata-backed protocol implementations selected by N158. @@ -53,67 +83,22 @@ TerminalCapabilityEvidenceLedger evidence ArgumentNullException.ThrowIfNull( terminal ); ArgumentNullException.ThrowIfNull( evidence ); - if ( HasExtendedString( - terminal, - ClipboardWriteCapability - ) ) { - AdvertiseSemantic( - evidence, - TerminalSemanticOperation.ClipboardWrite - ); - } - - if ( HasExtendedString( - terminal, - CursorStyleCapability - ) ) { - AdvertiseSemantic( - evidence, - TerminalSemanticOperation.CursorStyle - ); - } - - if ( terminal.GetBoolean( BooleanCapability.CanChangeColor ) - && !string.IsNullOrEmpty( - terminal.GetString( StringCapability.InitializeColor ) - ) ) { - AdvertiseSemantic( - evidence, - TerminalSemanticOperation.PaletteColor - ); - } - - if ( HasExtendedStringContract( - terminal, - FocusEnableCapability, - FocusDisableCapability, - FocusInCapability, - FocusOutCapability - ) ) { - AdvertiseBackend( - evidence, - TerminalProtocolBackend.CsiFocusReporting - ); - } - - if ( HasExtendedStringContract( - terminal, - BracketedPasteEnableCapability, - BracketedPasteDisableCapability, - BracketedPasteStartCapability, - BracketedPasteEndCapability - ) ) { - AdvertiseBackend( - evidence, - TerminalProtocolBackend.CsiBracketedPaste - ); + foreach ( ExactSemanticContract contract in ExactSemanticContracts ) { + if ( contract.IsAdvertised( terminal ) ) { + AdvertiseSemantic( + evidence, + contract.Operation + ); + } } - if ( HasAdvertisedMouseProtocol( terminal ) ) { - AdvertiseBackend( - evidence, - TerminalProtocolBackend.CsiMouseReporting - ); + foreach ( BackendAdvertisementContract contract in BackendAdvertisementContracts ) { + if ( contract.IsAdvertised( terminal ) ) { + AdvertiseBackend( + evidence, + contract.Backend + ); + } } } @@ -134,24 +119,12 @@ TerminalSemanticOperation operation ); } - return operation switch { - TerminalSemanticOperation.ClipboardWrite - => HasExtendedString( - terminal, - ClipboardWriteCapability - ), - TerminalSemanticOperation.CursorStyle - => HasExtendedString( - terminal, - CursorStyleCapability - ), - TerminalSemanticOperation.PaletteColor - => terminal.GetBoolean( BooleanCapability.CanChangeColor ) - && !string.IsNullOrEmpty( - terminal.GetString( StringCapability.InitializeColor ) - ), - _ => false - }; + foreach ( ExactSemanticContract contract in ExactSemanticContracts ) { + if ( contract.Operation == operation ) { + return contract.IsAdvertised( terminal ); + } + } + return false; } /// @@ -184,6 +157,67 @@ TerminalDescription terminal ); } + private static bool HasClipboardWrite( + TerminalDescription terminal + ) { + ArgumentNullException.ThrowIfNull( terminal ); + + return HasExtendedString( + terminal, + ClipboardWriteCapability + ); + } + + private static bool HasCursorStyle( + TerminalDescription terminal + ) { + ArgumentNullException.ThrowIfNull( terminal ); + + return HasExtendedString( + terminal, + CursorStyleCapability + ); + } + + private static bool HasPaletteColor( + TerminalDescription terminal + ) { + ArgumentNullException.ThrowIfNull( terminal ); + + return terminal.GetBoolean( BooleanCapability.CanChangeColor ) + && !string.IsNullOrEmpty( + terminal.GetString( StringCapability.InitializeColor ) + ); + } + + private static bool HasFocusReporting( + TerminalDescription terminal + ) { + ArgumentNullException.ThrowIfNull( terminal ); + + return HasExtendedStringContract( + terminal, + FocusEnableCapability, + FocusDisableCapability, + FocusInCapability, + FocusOutCapability + ); + } + + private static bool HasBracketedPaste( + TerminalDescription terminal + ) { + ArgumentNullException.ThrowIfNull( terminal ); + + return HasExtendedStringContract( + terminal, + BracketedPasteEnableCapability, + BracketedPasteDisableCapability, + BracketedPasteStartCapability, + BracketedPasteEndCapability + ); + } + private static void AdvertiseSemantic( TerminalCapabilityEvidenceLedger evidence, TerminalSemanticOperation operation @@ -241,4 +275,14 @@ string name out string? value ) && !string.IsNullOrEmpty( value ); } + + private readonly record struct ExactSemanticContract( + TerminalSemanticOperation Operation, + Func IsAdvertised + ); + + private readonly record struct BackendAdvertisementContract( + TerminalProtocolBackend Backend, + Func IsAdvertised + ); } From e910480f7b8345f1f9de8999b5783bca7aa7f853 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:45:55 -0400 Subject: [PATCH 26/63] test: define persistent raster source rectangle contract --- .../TerminalRasterSourceRectangleTests.cs | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterSourceRectangleTests.cs diff --git a/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterSourceRectangleTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterSourceRectangleTests.cs new file mode 100644 index 000000000..dc24129a1 --- /dev/null +++ b/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterSourceRectangleTests.cs @@ -0,0 +1,107 @@ +/* + Icod.Terminal.Tests + Automated test suite for the Icod.Terminal library. + Copyright (C) 2026 Timothy J. Bruce +*/ + +/* + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +namespace Icod.Terminal.Tests.Graphics; + +using Icod.Terminal; +using Xunit; + +/// +/// Verifies the public pixel-space source rectangle value contract. +/// +public sealed class TerminalRasterSourceRectangleTests { + [Fact] + public void ConstructorPreservesPixelRectangle() { + TerminalRasterSourceRectangle rectangle = new( + 2, + 3, + 4, + 5 + ); + + Assert.Equal( 2, rectangle.X ); + Assert.Equal( 3, rectangle.Y ); + Assert.Equal( 4, rectangle.Width ); + Assert.Equal( 5, rectangle.Height ); + } + + [Fact] + public void ConstructorAcceptsScalarBoundary() { + TerminalRasterSourceRectangle rectangle = new( + TerminalRasterImage.MaximumDimension - 1, + TerminalRasterImage.MaximumDimension - 1, + 1, + 1 + ); + + Assert.Equal( + TerminalRasterImage.MaximumDimension - 1, + rectangle.X + ); + Assert.Equal( + TerminalRasterImage.MaximumDimension - 1, + rectangle.Y + ); + } + + [Theory] + [InlineData( -1, 0, 1, 1 )] + [InlineData( 0, -1, 1, 1 )] + [InlineData( 0, 0, 0, 1 )] + [InlineData( 0, 0, -1, 1 )] + [InlineData( 0, 0, 1, 0 )] + [InlineData( 0, 0, 1, -1 )] + public void ConstructorRejectsNegativeCoordinatesAndNonPositiveExtents( + int x, + int y, + int width, + int height + ) { + _ = Assert.Throws( + () => new TerminalRasterSourceRectangle( + x, + y, + width, + height + ) + ); + } + + [Theory] + [InlineData( TerminalRasterImage.MaximumDimension, 0, 1, 1 )] + [InlineData( 0, TerminalRasterImage.MaximumDimension, 1, 1 )] + [InlineData( 0, 0, TerminalRasterImage.MaximumDimension + 1, 1 )] + [InlineData( 0, 0, 1, TerminalRasterImage.MaximumDimension + 1 )] + public void ConstructorRejectsScalarsBeyondMaximumDimension( + int x, + int y, + int width, + int height + ) { + _ = Assert.Throws( + () => new TerminalRasterSourceRectangle( + x, + y, + width, + height + ) + ); + } +} From 9a9fd5353610248de7d8b5ebf8617084bb502a16 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:47:48 -0400 Subject: [PATCH 27/63] feat: add persistent raster source rectangle value --- src/Graphics/TerminalRasterSourceRectangle.cs | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/Graphics/TerminalRasterSourceRectangle.cs diff --git a/src/Graphics/TerminalRasterSourceRectangle.cs b/src/Graphics/TerminalRasterSourceRectangle.cs new file mode 100644 index 000000000..c45b22b10 --- /dev/null +++ b/src/Graphics/TerminalRasterSourceRectangle.cs @@ -0,0 +1,118 @@ +/* + Icod.Terminal + Managed, cross-platform live-terminal session and terminal-control library for .NET. + Copyright (C) 2026 Timothy J. Bruce +*/ + +/* + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ +namespace Icod.Terminal; + +/// +/// Identifies a bounded rectangular region of source raster pixels for one persistent placement. +/// +public readonly struct TerminalRasterSourceRectangle { + /// + /// Initializes a source-pixel rectangle. + /// + /// The zero-based source-pixel column. + /// The zero-based source-pixel row. + /// The positive source-pixel width. + /// The positive source-pixel height. + public TerminalRasterSourceRectangle( + int x, + int y, + int width, + int height + ) { + ValidateCoordinate( + x, + nameof( x ) + ); + ValidateCoordinate( + y, + nameof( y ) + ); + ValidateExtent( + width, + nameof( width ) + ); + ValidateExtent( + height, + nameof( height ) + ); + + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; + } + + /// + /// Gets the zero-based source-pixel column. + /// + public int X { + get; + } + + /// + /// Gets the zero-based source-pixel row. + /// + public int Y { + get; + } + + /// + /// Gets the source-pixel width. + /// + public int Width { + get; + } + + /// + /// Gets the source-pixel height. + /// + public int Height { + get; + } + + private static void ValidateCoordinate( + int value, + string parameterName + ) { + ArgumentException.ThrowIfNullOrEmpty( parameterName ); + if ( value is < 0 or >= TerminalRasterImage.MaximumDimension ) { + throw new ArgumentOutOfRangeException( + parameterName, + value, + $"A raster source coordinate must be between 0 and {TerminalRasterImage.MaximumDimension - 1}." + ); + } + } + + private static void ValidateExtent( + int value, + string parameterName + ) { + ArgumentException.ThrowIfNullOrEmpty( parameterName ); + if ( value is < 1 or > TerminalRasterImage.MaximumDimension ) { + throw new ArgumentOutOfRangeException( + parameterName, + value, + $"A raster source extent must be between 1 and {TerminalRasterImage.MaximumDimension}." + ); + } + } +} From 58a4c149623538fcbb2c79218e98dd97c39daeb9 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:51:42 -0400 Subject: [PATCH 28/63] test: define source rectangle resource validation --- ...inalRasterPlacementSourceRectangleTests.cs | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterPlacementSourceRectangleTests.cs diff --git a/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterPlacementSourceRectangleTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterPlacementSourceRectangleTests.cs new file mode 100644 index 000000000..216d03c97 --- /dev/null +++ b/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterPlacementSourceRectangleTests.cs @@ -0,0 +1,149 @@ +/* + Icod.Terminal.Tests + Automated test suite for the Icod.Terminal library. + Copyright (C) 2026 Timothy J. Bruce +*/ + +/* + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +namespace Icod.Terminal.Tests.Graphics; + +using Icod.Terminal; +using Xunit; + +/// +/// Verifies resource-aware validation for persistent raster source rectangles. +/// +public sealed class TerminalRasterPlacementSourceRectangleTests { + [Fact] + public void PlacementOptionsExposeNullableSourceRectangle() { + TerminalRasterSourceRectangle rectangle = new( + 1, + 2, + 3, + 4 + ); + TerminalRasterPlacementOptions options = new() { + SourceRectangle = rectangle + }; + + Assert.Equal( rectangle, options.SourceRectangle ); + Assert.Equal( + typeof( TerminalRasterSourceRectangle? ), + typeof( TerminalRasterPlacementOptions ) + .GetProperty( nameof( TerminalRasterPlacementOptions.SourceRectangle ) )? + .PropertyType + ); + } + + [Fact] + public void FullSourceRectangleFitsResourceExactly() { + TerminalRasterPlacementOptions options = new() { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 4, + 3 + ) + }; + + options.Validate( + 4, + 3 + ); + } + + [Fact] + public void InteriorSourceRectangleFitsResource() { + TerminalRasterPlacementOptions options = new() { + SourceRectangle = new TerminalRasterSourceRectangle( + 1, + 1, + 3, + 2 + ) + }; + + options.Validate( + 4, + 3 + ); + } + + [Theory] + [InlineData( 2, 0, 3, 3 )] + [InlineData( 0, 2, 4, 2 )] + public void SourceRectangleCannotExtendBeyondResource( + int x, + int y, + int width, + int height + ) { + TerminalRasterPlacementOptions options = new() { + SourceRectangle = new TerminalRasterSourceRectangle( + x, + y, + width, + height + ) + }; + + _ = Assert.Throws( + () => options.Validate( + 4, + 3 + ) + ); + } + + [Fact] + public void NullSourceRectangleAcceptsValidResourceDimensions() { + TerminalRasterPlacementOptions options = new(); + + options.Validate( + 4, + 3 + ); + } + + [Fact] + public void ResourceStateRetainsSourceDimensionsOnlyAsMetadata() { + TerminalPersistentRasterResourceState state = new( + imageNumber: 1, + generation: 1, + sourceWidth: 4, + sourceHeight: 3 + ); + + Assert.Equal( 4, state.SourceWidth ); + Assert.Equal( 3, state.SourceHeight ); + } + + [Fact] + public void RegistryReservationRetainsSourceDimensions() { + TerminalPersistentRasterRegistry registry = new(); + + Assert.True( + registry.TryReserveResource( + 4, + 3, + out TerminalPersistentRasterResourceState? resource + ) + ); + Assert.NotNull( resource ); + Assert.Equal( 4, resource.SourceWidth ); + Assert.Equal( 3, resource.SourceHeight ); + } +} From f2f32589f3e47ef18fafce004913f7ce870f62cd Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 13:54:23 -0400 Subject: [PATCH 29/63] feat: add source rectangle placement option --- .../TerminalRasterPlacementOptions.cs | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/Graphics/TerminalRasterPlacementOptions.cs b/src/Graphics/TerminalRasterPlacementOptions.cs index cbf93a5b8..874b2d337 100644 --- a/src/Graphics/TerminalRasterPlacementOptions.cs +++ b/src/Graphics/TerminalRasterPlacementOptions.cs @@ -21,7 +21,7 @@ You should have received a copy of the GNU Lesser General Public License namespace Icod.Terminal; /// -/// Configures the cell extents of one persistent raster placement. +/// Configures the geometry of one persistent raster placement. /// public sealed class TerminalRasterPlacementOptions { /// @@ -42,6 +42,15 @@ public int? Rows { set; } + /// + /// Gets or sets the source-pixel rectangle to display, or + /// to place the complete source raster. + /// + public TerminalRasterSourceRectangle? SourceRectangle { + get; + set; + } + internal void Validate() { ValidateExtent( this.Columns, @@ -53,6 +62,36 @@ internal void Validate() { ); } + internal void Validate( + int sourceWidth, + int sourceHeight + ) { + ValidateSourceDimension( + sourceWidth, + nameof( sourceWidth ) + ); + ValidateSourceDimension( + sourceHeight, + nameof( sourceHeight ) + ); + this.Validate(); + + if ( !this.SourceRectangle.HasValue ) { + return; + } + + TerminalRasterSourceRectangle rectangle = this.SourceRectangle.Value; + long right = (long)rectangle.X + rectangle.Width; + long bottom = (long)rectangle.Y + rectangle.Height; + if ( sourceWidth < right || sourceHeight < bottom ) { + throw new ArgumentOutOfRangeException( + nameof( this.SourceRectangle ), + this.SourceRectangle, + "The raster source rectangle must fit completely inside the persistent raster resource." + ); + } + } + private static void ValidateExtent( int? value, string parameterName @@ -69,4 +108,18 @@ string parameterName ); } } + + private static void ValidateSourceDimension( + int value, + string parameterName + ) { + ArgumentException.ThrowIfNullOrEmpty( parameterName ); + if ( value is < 1 or > TerminalRasterImage.MaximumDimension ) { + throw new ArgumentOutOfRangeException( + parameterName, + value, + $"A persistent raster source dimension must be between 1 and {TerminalRasterImage.MaximumDimension}." + ); + } + } } From ffb4f9e798b09d3248e7335b58372c3d0d0b89a7 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 14:07:00 -0400 Subject: [PATCH 30/63] feat: retain source dimensions for raster placements --- .../TerminalPersistentRasterRegistry.cs | 23 +++++++++- .../TerminalPersistentRasterResourceState.cs | 45 +++++++++++++++++++ src/Graphics/TerminalRasterPlacement.cs | 7 ++- src/Graphics/TerminalRasterResource.cs | 7 ++- 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/Graphics/TerminalPersistentRasterRegistry.cs b/src/Graphics/TerminalPersistentRasterRegistry.cs index cf4288c66..69ff26a52 100644 --- a/src/Graphics/TerminalPersistentRasterRegistry.cs +++ b/src/Graphics/TerminalPersistentRasterRegistry.cs @@ -80,6 +80,25 @@ internal int LivePlacementCount { internal bool TryReserveResource( out TerminalPersistentRasterResourceState? resource ) { + return this.TryReserveResource( + TerminalRasterImage.MaximumDimension, + TerminalRasterImage.MaximumDimension, + out resource + ); + } + + internal bool TryReserveResource( + int sourceWidth, + int sourceHeight, + out TerminalPersistentRasterResourceState? resource + ) { + if ( sourceWidth is < 1 or > TerminalRasterImage.MaximumDimension ) { + throw new ArgumentOutOfRangeException( nameof( sourceWidth ) ); + } + if ( sourceHeight is < 1 or > TerminalRasterImage.MaximumDimension ) { + throw new ArgumentOutOfRangeException( nameof( sourceHeight ) ); + } + lock ( this.synchronization ) { if ( MaximumResources <= this.resources.Count ) { resource = null; @@ -92,7 +111,9 @@ ref this.nextImageNumber ); resource = new TerminalPersistentRasterResourceState( imageNumber, - this.generation + this.generation, + sourceWidth, + sourceHeight ); this.imageNumbers.Add( imageNumber ); this.resources.Add( diff --git a/src/Graphics/TerminalPersistentRasterResourceState.cs b/src/Graphics/TerminalPersistentRasterResourceState.cs index 087ff6db4..50c32b9a5 100644 --- a/src/Graphics/TerminalPersistentRasterResourceState.cs +++ b/src/Graphics/TerminalPersistentRasterResourceState.cs @@ -30,6 +30,19 @@ internal sealed class TerminalPersistentRasterResourceState { internal TerminalPersistentRasterResourceState( uint imageNumber, long generation + ) : this( + imageNumber, + generation, + TerminalRasterImage.MaximumDimension, + TerminalRasterImage.MaximumDimension + ) { + } + + internal TerminalPersistentRasterResourceState( + uint imageNumber, + long generation, + int sourceWidth, + int sourceHeight ) { if ( 0u == imageNumber ) { throw new ArgumentOutOfRangeException( nameof( imageNumber ) ); @@ -37,9 +50,19 @@ long generation if ( generation < 0 ) { throw new ArgumentOutOfRangeException( nameof( generation ) ); } + ValidateSourceDimension( + sourceWidth, + nameof( sourceWidth ) + ); + ValidateSourceDimension( + sourceHeight, + nameof( sourceHeight ) + ); this.ImageNumber = imageNumber; this.Generation = generation; + this.SourceWidth = sourceWidth; + this.SourceHeight = sourceHeight; } internal uint ImageNumber { @@ -56,6 +79,14 @@ internal long Generation { get; } + internal int SourceWidth { + get; + } + + internal int SourceHeight { + get; + } + internal bool IsClosed { get { return 0 != Volatile.Read( ref this.closed ); @@ -88,4 +119,18 @@ internal void Close() { 1 ); } + + private static void ValidateSourceDimension( + int value, + string parameterName + ) { + ArgumentException.ThrowIfNullOrEmpty( parameterName ); + if ( value is < 1 or > TerminalRasterImage.MaximumDimension ) { + throw new ArgumentOutOfRangeException( + parameterName, + value, + $"A persistent raster source dimension must be between 1 and {TerminalRasterImage.MaximumDimension}." + ); + } + } } diff --git a/src/Graphics/TerminalRasterPlacement.cs b/src/Graphics/TerminalRasterPlacement.cs index 6d22f793b..315fd675d 100644 --- a/src/Graphics/TerminalRasterPlacement.cs +++ b/src/Graphics/TerminalRasterPlacement.cs @@ -45,14 +45,17 @@ internal TerminalPersistentRasterPlacementState State { /// Replaces this placement at the terminal's current cursor position while retaining its /// private resource and placement identities. /// - /// Optional terminal-cell placement extents. + /// Optional persistent-raster placement geometry. /// Cancellation observed before replacement output commits. /// The controlled mutation result. public ValueTask UpdateAsync( TerminalRasterPlacementOptions? options = null, CancellationToken cancellationToken = default ) { - options?.Validate(); + options?.Validate( + this.State.Resource.SourceWidth, + this.State.Resource.SourceHeight + ); cancellationToken.ThrowIfCancellationRequested(); TerminalSession? owner = Volatile.Read( ref this.session ); diff --git a/src/Graphics/TerminalRasterResource.cs b/src/Graphics/TerminalRasterResource.cs index 512ed387a..3fc08ad0a 100644 --- a/src/Graphics/TerminalRasterResource.cs +++ b/src/Graphics/TerminalRasterResource.cs @@ -44,7 +44,7 @@ internal TerminalPersistentRasterResourceState State { /// /// Creates one opaque placement of this resource at the terminal's current cursor position. /// - /// Optional terminal-cell placement extents. + /// Optional persistent-raster placement geometry. /// Cancellation observed before placement output commits. /// /// An available opaque placement, or a controlled unavailable result when the session cannot @@ -54,7 +54,10 @@ public ValueTask> CreatePlacement TerminalRasterPlacementOptions? options = null, CancellationToken cancellationToken = default ) { - options?.Validate(); + options?.Validate( + this.State.SourceWidth, + this.State.SourceHeight + ); cancellationToken.ThrowIfCancellationRequested(); TerminalSession? owner = Volatile.Read( ref this.session ); From 4ac3de0a6c8b086a1e09ef2498cedb06ab1b90ed Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 14:07:46 -0400 Subject: [PATCH 31/63] test: require actual persistent raster source dimensions --- ...ntRasterSourceDimensionIntegrationTests.cs | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterSourceDimensionIntegrationTests.cs diff --git a/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterSourceDimensionIntegrationTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterSourceDimensionIntegrationTests.cs new file mode 100644 index 000000000..d18fe6e55 --- /dev/null +++ b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterSourceDimensionIntegrationTests.cs @@ -0,0 +1,137 @@ +/* + Icod.Terminal.Tests + Automated test suite for the Icod.Terminal library. + Copyright (C) 2026 Timothy J. Bruce +*/ + +/* + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +namespace Icod.Terminal.Tests.Graphics; + +using System.Text; +using System.Threading.Channels; +using Icod.Terminal; +using Icod.TermInfo; +using Xunit; + +/// +/// Verifies that live persistent-resource creation retains the actual source dimensions. +/// +public sealed class TerminalPersistentRasterSourceDimensionIntegrationTests { + [Fact] + public async Task CreatedResourceRetainsActualSourceDimensions() { + ScriptedTransport transport = new(); + await using TerminalSession session = await TerminalSession.OpenAsync( + new RecordingTerminalControlProvider(), + TerminalEndpoint.StandardInput, + TerminalEndpoint.StandardOutput, + transport, + transport, + new TerminalSessionOptions { + TerminalOverride = TerminalProfiles.Dumb, + ConfigureOutput = false, + ObserveLifecycleEvents = false, + RequireInteractiveOutput = false + } + ); + session.RecordSemanticBackendEvidence( + TerminalProtocolBackend.ApcKittyGraphics, + TerminalCapabilitySupportState.Verified, + TerminalCapabilityEvidenceSource.ProtocolResponse + ); + TerminalRasterImage image = TerminalRasterImage.CreateRgb24( + 2, + 3, + new byte[ 18 ] + ); + + Task> creation = + session.CreateRasterResourceAsync( image ).AsTask(); + await transport.WaitForWriteCountAsync( 1 ); + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=77,I=1;OK\u001b\\" ) + ); + TerminalControlResult result = await creation; + TerminalRasterResource resource = Assert.IsType( + result.Value + ); + + Assert.Equal( 2, resource.State.SourceWidth ); + Assert.Equal( 3, resource.State.SourceHeight ); + } + + private sealed class ScriptedTransport : ITerminalInput, ITerminalOutput { + private readonly Channel input = Channel.CreateUnbounded( + new UnboundedChannelOptions { + SingleReader = true, + SingleWriter = false, + AllowSynchronousContinuations = false + } + ); + private readonly SemaphoreSlim writeSignal = new( 0 ); + private int writeCount; + + public async ValueTask ReadAsync( + Memory buffer, + CancellationToken cancellationToken = default + ) { + byte[] value = await this.input.Reader.ReadAsync( + cancellationToken + ).ConfigureAwait( false ); + value.AsSpan().CopyTo( buffer.Span ); + return value.Length; + } + + public ValueTask WriteAsync( + ReadOnlyMemory buffer, + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + _ = Interlocked.Increment( ref this.writeCount ); + this.writeSignal.Release(); + return ValueTask.CompletedTask; + } + + public ValueTask FlushAsync( + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + return ValueTask.CompletedTask; + } + + internal void Publish( + byte[] bytes + ) { + ArgumentNullException.ThrowIfNull( bytes ); + if ( !this.input.Writer.TryWrite( bytes.ToArray() ) ) { + throw new InvalidOperationException( + "The scripted terminal input channel is closed." + ); + } + } + + internal async Task WaitForWriteCountAsync( + int expected + ) { + if ( 0 > expected ) { + throw new ArgumentOutOfRangeException( nameof( expected ) ); + } + + while ( Volatile.Read( ref this.writeCount ) < expected ) { + await this.writeSignal.WaitAsync().ConfigureAwait( false ); + } + } + } +} From d668dc41ecdcc35824b553238cf924f9bc06783f Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 14:25:16 -0400 Subject: [PATCH 32/63] fix: preserve uploaded raster source dimensions --- .../TerminalSession.PersistentRasterGraphics.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Session/TerminalSession.PersistentRasterGraphics.cs b/src/Session/TerminalSession.PersistentRasterGraphics.cs index 9442485ca..0485ac5dd 100644 --- a/src/Session/TerminalSession.PersistentRasterGraphics.cs +++ b/src/Session/TerminalSession.PersistentRasterGraphics.cs @@ -73,6 +73,8 @@ public async ValueTask> CreateRast KittyRasterData raster = KittyRasterAdapter.Adapt( image ); if ( !this.persistentRasterRegistry.TryReserveResource( + raster.Width, + raster.Height, out TerminalPersistentRasterResourceState? resourceState ) ) { return TerminalControlResult.Unavailable( @@ -163,7 +165,10 @@ internal async ValueTask> CreateP CancellationToken cancellationToken ) { ArgumentNullException.ThrowIfNull( resourceState ); - options?.Validate(); + options?.Validate( + resourceState.SourceWidth, + resourceState.SourceHeight + ); cancellationToken.ThrowIfCancellationRequested(); this.ThrowIfSessionOutputClosed(); if ( resourceState.IsClosed ) { @@ -292,7 +297,10 @@ internal async ValueTask UpdatePersistentRasterPl CancellationToken cancellationToken ) { ArgumentNullException.ThrowIfNull( placementState ); - options?.Validate(); + options?.Validate( + placementState.Resource.SourceWidth, + placementState.Resource.SourceHeight + ); cancellationToken.ThrowIfCancellationRequested(); this.ThrowIfSessionOutputClosed(); From 72ecbcb85209a260e0b9f1bc4729db1b8a106210 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 14:32:39 -0400 Subject: [PATCH 33/63] test: require signed persistent raster z-order --- .../TerminalRasterPlacementZIndexTests.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterPlacementZIndexTests.cs diff --git a/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterPlacementZIndexTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterPlacementZIndexTests.cs new file mode 100644 index 000000000..6dee4d45c --- /dev/null +++ b/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterPlacementZIndexTests.cs @@ -0,0 +1,52 @@ +/* + Icod.Terminal.Tests + Automated test suite for the Icod.Terminal library. + Copyright (C) 2026 Timothy J. Bruce +*/ + +/* + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +namespace Icod.Terminal.Tests.Graphics; + +using Icod.Terminal; +using Xunit; + +/// +/// Verifies the public signed z-order contract for persistent raster placements. +/// +public sealed class TerminalRasterPlacementZIndexTests { + [Theory] + [InlineData( 0 )] + [InlineData( -1 )] + [InlineData( 1 )] + [InlineData( int.MinValue )] + [InlineData( int.MaxValue )] + public void ZIndexPreservesSignedIntValues( + int value + ) { + TerminalRasterPlacementOptions options = new() { + ZIndex = value + }; + + Assert.Equal( value, options.ZIndex ); + } + + [Fact] + public void ZIndexDefaultsToNull() { + TerminalRasterPlacementOptions options = new(); + + Assert.Null( options.ZIndex ); + } +} From 33a4e5b622370000719ff49f9b86b75714d8222a Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 14:34:02 -0400 Subject: [PATCH 34/63] feat: add persistent raster z-order option --- src/Graphics/TerminalRasterPlacementOptions.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Graphics/TerminalRasterPlacementOptions.cs b/src/Graphics/TerminalRasterPlacementOptions.cs index 874b2d337..c806f1bf7 100644 --- a/src/Graphics/TerminalRasterPlacementOptions.cs +++ b/src/Graphics/TerminalRasterPlacementOptions.cs @@ -51,6 +51,15 @@ public TerminalRasterSourceRectangle? SourceRectangle { set; } + /// + /// Gets or sets the signed placement z-order, or to use the + /// terminal backend's default placement order. + /// + public int? ZIndex { + get; + set; + } + internal void Validate() { ValidateExtent( this.Columns, From 98e02a7559cba298691bafe9b4295d7a93bb4287 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 14:40:07 -0400 Subject: [PATCH 35/63] docs: freeze 1.12 development API fingerprint --- docs/Public-API-Baseline-1.12.sha256 | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/Public-API-Baseline-1.12.sha256 diff --git a/docs/Public-API-Baseline-1.12.sha256 b/docs/Public-API-Baseline-1.12.sha256 new file mode 100644 index 000000000..58b83b51a --- /dev/null +++ b/docs/Public-API-Baseline-1.12.sha256 @@ -0,0 +1 @@ +eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8 From d25e291fb17118f740d4714e1956a3db1e86eb9d Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 14:40:19 -0400 Subject: [PATCH 36/63] docs: record 1.12 development API baseline --- docs/Public-API-Baseline-1.12.md | 66 ++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 docs/Public-API-Baseline-1.12.md diff --git a/docs/Public-API-Baseline-1.12.md b/docs/Public-API-Baseline-1.12.md new file mode 100644 index 000000000..40e5e08aa --- /dev/null +++ b/docs/Public-API-Baseline-1.12.md @@ -0,0 +1,66 @@ +# Icod.Terminal Public API Baseline — 1.12.0 + +**Release:** `1.12.0` +**Status:** development API freeze after T123 +**Target frameworks:** `net8.0`, `net9.0`, `net10.0` + +## Purpose + +This document records the reviewed additive public surface planned for `Icod.Terminal 1.12.0` after T122 source rectangles and T123 signed z-order. The historical 1.11 baseline remains unchanged as compatibility evidence. + +No further public API additions are planned for T124–T127. Later tranches implement, harden, document, package, and qualify this surface. + +## Public additions over 1.11 + +Version 1.12 adds the backend-neutral immutable source rectangle: + +```csharp +public readonly struct TerminalRasterSourceRectangle { + public TerminalRasterSourceRectangle( + int x, + int y, + int width, + int height + ); + + public int X { get; } + public int Y { get; } + public int Width { get; } + public int Height { get; } +} +``` + +`TerminalRasterPlacementOptions` gains: + +```csharp +public TerminalRasterSourceRectangle? SourceRectangle { get; set; } +public int? ZIndex { get; set; } +``` + +`SourceRectangle` uses zero-based source-image pixel coordinates. Its scalar contract requires non-negative `X`/`Y`, positive `Width`/`Height`, and values bounded by `TerminalRasterImage.MaximumDimension`; placement creation/update additionally require the rectangle to fit completely inside the actual uploaded source raster. + +`ZIndex` accepts the complete signed 32-bit `int` domain. `null` retains backend/default order. + +No new `TerminalCapability` value, public backend selector, Kitty identity, raw command surface, relative placement graph, Unicode placeholder contract, or animation/frame lifecycle is introduced. + +## Machine fingerprint + +The deterministic reflection snapshot is identical across `net8.0`, `net9.0`, and `net10.0`. + +After normalizing line endings to LF, the reviewed 1.12 development fingerprint is: + +```text +eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8 +``` + +The machine-readable fingerprint is stored in: + +`docs/Public-API-Baseline-1.12.sha256` + +`packaging/VerifyPublicApiBaseline.ps1` regenerates the public API snapshot independently for every supported target framework and verifies this exact fingerprint. + +## Compatibility meaning + +The 1.12 surface is additive over the stable `1.0.0` compatibility floor and preserves all existing public signatures and enum numeric values. Existing 1.11 placement behavior remains unchanged when `SourceRectangle` and `ZIndex` are omitted. + +T127 must re-run the exact public snapshot and confirm this fingerprint is unchanged before stable release. Any later public API drift requires explicit review rather than silently changing this baseline. From c22a2e69b1528cbcc49dc9d2f6f09a36f72fb571 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 14:40:34 -0400 Subject: [PATCH 37/63] build: advance public API verifier to 1.12 --- packaging/VerifyPublicApiBaseline.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/VerifyPublicApiBaseline.ps1 b/packaging/VerifyPublicApiBaseline.ps1 index 1dbb88d16..ed4d878a0 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.11.sha256' + [string]$BaselinePath = 'docs/Public-API-Baseline-1.12.sha256' ) Set-StrictMode -Version Latest From 5d8be1bc89cd05b63142c9179b52356effe0dad9 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 14:52:19 -0400 Subject: [PATCH 38/63] test: specify advanced raster placement encoding --- ...PersistentAdvancedPlacementEncoderTests.cs | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementEncoderTests.cs diff --git a/tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementEncoderTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementEncoderTests.cs new file mode 100644 index 000000000..581ac9dca --- /dev/null +++ b/tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementEncoderTests.cs @@ -0,0 +1,135 @@ +/* + Icod.Terminal.Tests + Automated test suite for the Icod.Terminal library. + Copyright (C) 2026 Timothy J. Bruce +*/ + +/* + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +namespace Icod.Terminal.Tests.Graphics; + +using System.Text; +using Icod.Terminal; +using Xunit; + +/// +/// Verifies T124 deterministic advanced persistent-placement encoding. +/// +public sealed class KittyGraphicsPersistentAdvancedPlacementEncoderTests { + [Fact] + public void AdvancedPlacementUsesDeterministicGeometryOrder() { + TerminalRasterPlacementOptions options = new() { + SourceRectangle = new TerminalRasterSourceRectangle( + 2, + 3, + 4, + 5 + ), + Columns = 6, + Rows = 7, + ZIndex = -1 + }; + + ReadOnlyMemory payload = KittyGraphicsPersistentEncoder.EncodePlacementPayload( + imageId: 99, + placementId: 7, + options + ); + + Assert.Equal( + "Ga=p,i=99,p=7,C=1,x=2,y=3,w=4,h=5,c=6,r=7,z=-1", + Encoding.ASCII.GetString( payload.Span ) + ); + } + + [Fact] + public void NullAdvancedGeometryPreservesLegacyPlacementBytes() { + TerminalRasterPlacementOptions options = new() { + Columns = 3, + Rows = 2 + }; + + ReadOnlyMemory payload = KittyGraphicsPersistentEncoder.EncodePlacementPayload( + imageId: 99, + placementId: 7, + options + ); + + Assert.Equal( + "Ga=p,i=99,p=7,C=1,c=3,r=2", + Encoding.ASCII.GetString( payload.Span ) + ); + } + + [Fact] + public void NullOptionsPreserveIntrinsicLegacyPlacementBytes() { + ReadOnlyMemory payload = KittyGraphicsPersistentEncoder.EncodePlacementPayload( + imageId: 99, + placementId: 7, + options: null + ); + + Assert.Equal( + "Ga=p,i=99,p=7,C=1", + Encoding.ASCII.GetString( payload.Span ) + ); + } + + [Theory] + [InlineData( -1, "Ga=p,i=99,p=7,C=1,z=-1" )] + [InlineData( int.MinValue, "Ga=p,i=99,p=7,C=1,z=-2147483648" )] + [InlineData( 0, "Ga=p,i=99,p=7,C=1,z=0" )] + [InlineData( int.MaxValue, "Ga=p,i=99,p=7,C=1,z=2147483647" )] + public void ZIndexUsesInvariantSignedDecimal( + int zIndex, + string expected + ) { + TerminalRasterPlacementOptions options = new() { + ZIndex = zIndex + }; + + ReadOnlyMemory payload = KittyGraphicsPersistentEncoder.EncodePlacementPayload( + imageId: 99, + placementId: 7, + options + ); + + Assert.Equal( + expected, + Encoding.ASCII.GetString( payload.Span ) + ); + } + + [Fact] + public void MissingSourceRectangleEmitsNoSourceKeys() { + TerminalRasterPlacementOptions options = new() { + Columns = 2, + ZIndex = 3 + }; + + string payload = Encoding.ASCII.GetString( + KittyGraphicsPersistentEncoder.EncodePlacementPayload( + imageId: 99, + placementId: 7, + options + ).Span + ); + + Assert.DoesNotContain( ",x=", payload, StringComparison.Ordinal ); + Assert.DoesNotContain( ",y=", payload, StringComparison.Ordinal ); + Assert.DoesNotContain( ",w=", payload, StringComparison.Ordinal ); + Assert.DoesNotContain( ",h=", payload, StringComparison.Ordinal ); + } +} From cb3db8ff6f24a9da4733f85515dc43f3d3908b5a Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 14:54:33 -0400 Subject: [PATCH 39/63] feat: encode advanced raster placement geometry --- .../KittyGraphicsPersistentEncoder.cs | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/src/Graphics/KittyGraphicsPersistentEncoder.cs b/src/Graphics/KittyGraphicsPersistentEncoder.cs index 77159273b..f54c1fdcf 100644 --- a/src/Graphics/KittyGraphicsPersistentEncoder.cs +++ b/src/Graphics/KittyGraphicsPersistentEncoder.cs @@ -49,8 +49,7 @@ uint imageNumber internal static ReadOnlyMemory EncodePlacementPayload( uint imageId, uint placementId, - int? columns, - int? rows + TerminalRasterPlacementOptions? options ) { ValidateNonZeroIdentity( imageId, @@ -62,14 +61,7 @@ internal static ReadOnlyMemory EncodePlacementPayload( nameof( placementId ), "A persistent Kitty Graphics placement id must be non-zero." ); - ValidatePlacementExtent( - columns, - nameof( columns ) - ); - ValidatePlacementExtent( - rows, - nameof( rows ) - ); + options?.Validate(); StringBuilder value = new(); _ = value.Append( "Ga=p,i=" ); @@ -77,17 +69,47 @@ internal static ReadOnlyMemory EncodePlacementPayload( _ = value.Append( ",p=" ); _ = value.Append( placementId.ToString( CultureInfo.InvariantCulture ) ); _ = value.Append( ",C=1" ); - if ( columns.HasValue ) { + if ( options?.SourceRectangle is TerminalRasterSourceRectangle rectangle ) { + _ = value.Append( ",x=" ); + _ = value.Append( rectangle.X.ToString( CultureInfo.InvariantCulture ) ); + _ = value.Append( ",y=" ); + _ = value.Append( rectangle.Y.ToString( CultureInfo.InvariantCulture ) ); + _ = value.Append( ",w=" ); + _ = value.Append( rectangle.Width.ToString( CultureInfo.InvariantCulture ) ); + _ = value.Append( ",h=" ); + _ = value.Append( rectangle.Height.ToString( CultureInfo.InvariantCulture ) ); + } + if ( options?.Columns is int columns ) { _ = value.Append( ",c=" ); - _ = value.Append( columns.Value.ToString( CultureInfo.InvariantCulture ) ); + _ = value.Append( columns.ToString( CultureInfo.InvariantCulture ) ); } - if ( rows.HasValue ) { + if ( options?.Rows is int rows ) { _ = value.Append( ",r=" ); - _ = value.Append( rows.Value.ToString( CultureInfo.InvariantCulture ) ); + _ = value.Append( rows.ToString( CultureInfo.InvariantCulture ) ); + } + if ( options?.ZIndex is int zIndex ) { + _ = value.Append( ",z=" ); + _ = value.Append( zIndex.ToString( CultureInfo.InvariantCulture ) ); } return Encoding.ASCII.GetBytes( value.ToString() ); } + internal static ReadOnlyMemory EncodePlacementPayload( + uint imageId, + uint placementId, + int? columns, + int? rows + ) { + return EncodePlacementPayload( + imageId, + placementId, + new TerminalRasterPlacementOptions { + Columns = columns, + Rows = rows + } + ); + } + internal static ReadOnlyMemory EncodeDeletePlacementPayload( uint imageId, uint placementId From 330e496fe062b39a676539d39d475c603a5a31bc Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 15:00:03 -0400 Subject: [PATCH 40/63] test: require advanced placement transaction geometry --- ...istentAdvancedPlacementTransactionTests.cs | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementTransactionTests.cs diff --git a/tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementTransactionTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementTransactionTests.cs new file mode 100644 index 000000000..121b8c6f5 --- /dev/null +++ b/tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementTransactionTests.cs @@ -0,0 +1,140 @@ +/* + Icod.Terminal.Tests + Automated test suite for the Icod.Terminal library. + Copyright (C) 2026 Timothy J. Bruce +*/ + +/* + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +namespace Icod.Terminal.Tests.Graphics; + +using System.Text; +using System.Threading.Channels; +using Icod.Terminal; +using Xunit; + +/// +/// Verifies that the shared acknowledged placement transaction carries advanced geometry. +/// +public sealed class KittyGraphicsPersistentAdvancedPlacementTransactionTests { + [Fact] + public async Task SharedTransactionCarriesAdvancedGeometry() { + ScriptedTransport transport = new(); + await using TerminalSession session = await OpenSessionAsync( transport ); + TerminalRasterPlacementOptions options = new() { + SourceRectangle = new TerminalRasterSourceRectangle( + 2, + 3, + 4, + 5 + ), + Columns = 6, + Rows = 7, + ZIndex = -1 + }; + + await KittyGraphicsPersistentPlacementTransaction.WriteAsync( + session, + imageId: 99, + placementId: 7, + options, + CancellationToken.None + ); + + byte[] frame = Assert.Single( transport.Writes ); + Assert.Equal( + Encoding.ASCII.GetBytes( + "\u001b_Ga=p,i=99,p=7,C=1,x=2,y=3,w=4,h=5,c=6,r=7,z=-1\u001b\\" + ), + frame + ); + } + + private static ValueTask OpenSessionAsync( + ScriptedTransport transport + ) { + ArgumentNullException.ThrowIfNull( transport ); + return TerminalSession.OpenAsync( + new RecordingTerminalControlProvider(), + TerminalEndpoint.StandardInput, + TerminalEndpoint.StandardOutput, + transport, + transport, + new TerminalSessionOptions { + TerminalOverride = TerminalProfiles.Dumb, + ConfigureOutput = false, + ObserveLifecycleEvents = false, + RequireInteractiveOutput = false + } + ); + } + + private sealed class ScriptedTransport : ITerminalInput, ITerminalOutput { + private readonly Channel input = Channel.CreateUnbounded( + new UnboundedChannelOptions { + SingleReader = true, + SingleWriter = false, + AllowSynchronousContinuations = false + } + ); + private readonly object synchronization = new(); + private readonly List writes = []; + + internal IReadOnlyList Writes { + get { + lock ( this.synchronization ) { + return this.writes.Select( + static item => item.ToArray() + ).ToArray(); + } + } + } + + public async ValueTask ReadAsync( + Memory buffer, + CancellationToken cancellationToken = default + ) { + byte[] value = await this.input.Reader.ReadAsync( + cancellationToken + ).ConfigureAwait( false ); + if ( value.Length > buffer.Length ) { + throw new InvalidOperationException( + "The scripted response exceeds the terminal input buffer." + ); + } + + value.AsSpan().CopyTo( buffer.Span ); + return value.Length; + } + + public ValueTask WriteAsync( + ReadOnlyMemory buffer, + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + lock ( this.synchronization ) { + this.writes.Add( buffer.ToArray() ); + } + return ValueTask.CompletedTask; + } + + public ValueTask FlushAsync( + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + return ValueTask.CompletedTask; + } + } +} From 01d6c557098b7b51e872ed8d88f45e5d8fd1ae25 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 15:01:46 -0400 Subject: [PATCH 41/63] test: fix advanced placement transaction harness --- .../KittyGraphicsPersistentAdvancedPlacementTransactionTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementTransactionTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementTransactionTests.cs index 121b8c6f5..0a9412d49 100644 --- a/tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementTransactionTests.cs +++ b/tests/Icod.Terminal.Tests/src/Graphics/KittyGraphicsPersistentAdvancedPlacementTransactionTests.cs @@ -23,6 +23,7 @@ namespace Icod.Terminal.Tests.Graphics; using System.Text; using System.Threading.Channels; using Icod.Terminal; +using Icod.TermInfo; using Xunit; /// From 05784f853371396f2ff2e452af2d762e54f161c9 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 15:04:30 -0400 Subject: [PATCH 42/63] test: require public advanced placement lifecycle encoding --- ...RasterAdvancedPlacementIntegrationTests.cs | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedPlacementIntegrationTests.cs diff --git a/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedPlacementIntegrationTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedPlacementIntegrationTests.cs new file mode 100644 index 000000000..da00d5356 --- /dev/null +++ b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedPlacementIntegrationTests.cs @@ -0,0 +1,224 @@ +/* + Icod.Terminal.Tests + Automated test suite for the Icod.Terminal library. + Copyright (C) 2026 Timothy J. Bruce +*/ + +/* + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +namespace Icod.Terminal.Tests.Graphics; + +using System.Text; +using System.Threading.Channels; +using Icod.Terminal; +using Icod.TermInfo; +using Xunit; + +/// +/// Verifies advanced placement geometry through the public persistent-raster lifecycle. +/// +public sealed class TerminalPersistentRasterAdvancedPlacementIntegrationTests { + [Fact] + public async Task CreateAndUpdateCarryAdvancedGeometryWithStablePlacementIdentity() { + ScriptedTransport transport = new(); + await using TerminalSession session = await OpenSessionAsync( transport ); + TerminalRasterImage image = TerminalRasterImage.CreateRgb24( + 8, + 8, + new byte[ 8 * 8 * 3 ] + ); + + Task> resourceCreation = + session.CreateRasterResourceAsync( image ).AsTask(); + await transport.WaitForWriteCountAsync( 1 ); + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=77,I=1;OK\u001b\\" ) + ); + TerminalRasterResource resource = Assert.IsType( + ( await resourceCreation ).Value + ); + await using ( resource ) { + TerminalRasterPlacementOptions createOptions = new() { + SourceRectangle = new TerminalRasterSourceRectangle( + 1, + 2, + 4, + 5 + ), + Columns = 6, + Rows = 7, + ZIndex = -3 + }; + + Task> placementCreation = + resource.CreatePlacementAsync( createOptions ).AsTask(); + await transport.WaitForWriteCountAsync( 2 ); + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=77,p=1;OK\u001b\\" ) + ); + TerminalRasterPlacement placement = Assert.IsType( + ( await placementCreation ).Value + ); + await using ( placement ) { + Assert.Equal( + Encoding.ASCII.GetBytes( + "\u001b_Ga=p,i=77,p=1,C=1,x=1,y=2,w=4,h=5,c=6,r=7,z=-3\u001b\\" + ), + transport.Writes[ 1 ] + ); + + TerminalRasterPlacementOptions updateOptions = new() { + SourceRectangle = new TerminalRasterSourceRectangle( + 2, + 1, + 3, + 4 + ), + Columns = 5, + Rows = 4, + ZIndex = int.MinValue + }; + + Task update = placement.UpdateAsync( + updateOptions + ).AsTask(); + await transport.WaitForWriteCountAsync( 3 ); + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=77,p=1;OK\u001b\\" ) + ); + TerminalControlMutationResult updateResult = await update; + + Assert.True( updateResult.Succeeded ); + Assert.Equal( + Encoding.ASCII.GetBytes( + "\u001b_Ga=p,i=77,p=1,C=1,x=2,y=1,w=3,h=4,c=5,r=4,z=-2147483648\u001b\\" + ), + transport.Writes[ 2 ] + ); + } + } + } + + private static async ValueTask OpenSessionAsync( + ScriptedTransport transport + ) { + ArgumentNullException.ThrowIfNull( transport ); + TerminalSession session = await TerminalSession.OpenAsync( + new RecordingTerminalControlProvider(), + TerminalEndpoint.StandardInput, + TerminalEndpoint.StandardOutput, + transport, + transport, + new TerminalSessionOptions { + TerminalOverride = TerminalProfiles.Dumb, + ConfigureOutput = false, + ObserveLifecycleEvents = false, + RequireInteractiveOutput = false + } + ); + session.RecordSemanticBackendEvidence( + TerminalProtocolBackend.ApcKittyGraphics, + TerminalCapabilitySupportState.Verified, + TerminalCapabilityEvidenceSource.ProtocolResponse + ); + return session; + } + + private sealed class ScriptedTransport : ITerminalInput, ITerminalOutput { + private readonly Channel input = Channel.CreateUnbounded( + new UnboundedChannelOptions { + SingleReader = true, + SingleWriter = false, + AllowSynchronousContinuations = false + } + ); + private readonly object synchronization = new(); + private readonly SemaphoreSlim writeSignal = new( 0 ); + private readonly List writes = []; + + internal IReadOnlyList Writes { + get { + lock ( this.synchronization ) { + return this.writes.Select( + static value => value.ToArray() + ).ToArray(); + } + } + } + + public async ValueTask ReadAsync( + Memory buffer, + CancellationToken cancellationToken = default + ) { + byte[] value = await this.input.Reader.ReadAsync( + cancellationToken + ).ConfigureAwait( false ); + if ( value.Length > buffer.Length ) { + throw new InvalidOperationException( + "The scripted response exceeds the terminal input buffer." + ); + } + + value.AsSpan().CopyTo( buffer.Span ); + return value.Length; + } + + public ValueTask WriteAsync( + ReadOnlyMemory buffer, + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + lock ( this.synchronization ) { + this.writes.Add( buffer.ToArray() ); + } + this.writeSignal.Release(); + return ValueTask.CompletedTask; + } + + public ValueTask FlushAsync( + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + return ValueTask.CompletedTask; + } + + internal void Publish( + byte[] value + ) { + ArgumentNullException.ThrowIfNull( value ); + if ( !this.input.Writer.TryWrite( value.ToArray() ) ) { + throw new InvalidOperationException( + "The scripted terminal input channel rejected a response." + ); + } + } + + internal async Task WaitForWriteCountAsync( + int count + ) { + if ( 0 > count ) { + throw new ArgumentOutOfRangeException( nameof( count ) ); + } + + using CancellationTokenSource timeout = new(); + timeout.CancelAfter( TimeSpan.FromSeconds( 5 ) ); + while ( this.Writes.Count < count ) { + await this.writeSignal.WaitAsync( + timeout.Token + ).ConfigureAwait( false ); + } + } + } +} From aba1f7c0989d2c451294edf75590637c227a7e05 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 15:06:20 -0400 Subject: [PATCH 43/63] feat: route advanced geometry through placement transaction --- src/Graphics/KittyGraphicsPersistentPlacementTransaction.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Graphics/KittyGraphicsPersistentPlacementTransaction.cs b/src/Graphics/KittyGraphicsPersistentPlacementTransaction.cs index 9e66859a1..7d5e3669e 100644 --- a/src/Graphics/KittyGraphicsPersistentPlacementTransaction.cs +++ b/src/Graphics/KittyGraphicsPersistentPlacementTransaction.cs @@ -60,8 +60,7 @@ internal static async ValueTask WriteCoreAsync( ReadOnlyMemory payload = KittyGraphicsPersistentEncoder.EncodePlacementPayload( imageId, placementId, - options?.Columns, - options?.Rows + options ); byte[] frame = ApcWriter.EncodeFrame( payload.Span ); From c622841f92b9f17b0c1e07c7c68c30bbb997e45b Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 15:12:57 -0400 Subject: [PATCH 44/63] test: harden advanced raster placement boundaries --- ...lPersistentRasterAdvancedHardeningTests.cs | 517 ++++++++++++++++++ 1 file changed, 517 insertions(+) create mode 100644 tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedHardeningTests.cs diff --git a/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedHardeningTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedHardeningTests.cs new file mode 100644 index 000000000..3ed7d3581 --- /dev/null +++ b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedHardeningTests.cs @@ -0,0 +1,517 @@ +/* + Icod.Terminal.Tests + Automated test suite for the Icod.Terminal library. + Copyright (C) 2026 Timothy J. Bruce +*/ + +/* + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +namespace Icod.Terminal.Tests.Graphics; + +using System.Globalization; +using System.Text; +using System.Threading.Channels; +using Icod.Terminal; +using Icod.TermInfo; +using Xunit; + +/// +/// Verifies T125 boundary, acknowledgement, and stale-lifecycle semantics for advanced placements. +/// +public sealed class TerminalPersistentRasterAdvancedHardeningTests { + [Fact] + public async Task ExactEdgeGeometryCarriesBothZIndexExtrema() { + AcknowledgingTransport transport = new(); + await using TerminalSession session = await OpenSessionAsync( transport ); + TerminalRasterResource resource = await CreateResourceAsync( + session, + 4, + 3 + ); + await using ( resource ) { + TerminalControlResult creation = + await resource.CreatePlacementAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 1, + 1, + 3, + 2 + ), + Columns = 4, + Rows = 3, + ZIndex = int.MaxValue + } + ); + TerminalRasterPlacement placement = Assert.IsType( + creation.Value + ); + await using ( placement ) { + Assert.Equal( + Encoding.ASCII.GetBytes( + "\u001b_Ga=p,i=1001,p=1,C=1,x=1,y=1,w=3,h=2,c=4,r=3,z=2147483647\u001b\\" + ), + transport.Writes[ 1 ] + ); + + TerminalControlMutationResult update = await placement.UpdateAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 4, + 3 + ), + Columns = 1, + Rows = 1, + ZIndex = int.MinValue + } + ); + + Assert.True( update.Succeeded ); + Assert.Equal( + Encoding.ASCII.GetBytes( + "\u001b_Ga=p,i=1001,p=1,C=1,x=0,y=0,w=4,h=3,c=1,r=1,z=-2147483648\u001b\\" + ), + transport.Writes[ 2 ] + ); + } + } + } + + [Fact] + public async Task InvalidCreateAndUpdateRectanglesProduceNoOutput() { + AcknowledgingTransport transport = new(); + await using TerminalSession session = await OpenSessionAsync( transport ); + TerminalRasterResource resource = await CreateResourceAsync( + session, + 4, + 3 + ); + await using ( resource ) { + int beforeInvalidCreate = transport.Writes.Count; + await Assert.ThrowsAsync( + async () => await resource.CreatePlacementAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 2, + 0, + 3, + 3 + ) + } + ) + ); + Assert.Equal( beforeInvalidCreate, transport.Writes.Count ); + + TerminalControlResult creation = + await resource.CreatePlacementAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 4, + 3 + ), + ZIndex = 0 + } + ); + TerminalRasterPlacement placement = Assert.IsType( + creation.Value + ); + await using ( placement ) { + int beforeInvalidUpdate = transport.Writes.Count; + await Assert.ThrowsAsync( + async () => await placement.UpdateAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 2, + 4, + 2 + ), + ZIndex = 1 + } + ) + ); + Assert.Equal( beforeInvalidUpdate, transport.Writes.Count ); + } + } + } + + [Fact] + public async Task AdvancedGeometryPreservesAcknowledgementIdentityAndFailureSemantics() { + AcknowledgingTransport transport = new() { + AutoAcknowledgePlacements = false + }; + await using TerminalSession session = await OpenSessionAsync( transport ); + TerminalRasterResource resource = await CreateResourceAsync( + session, + 1, + 1 + ); + + TerminalRasterPlacementOptions createOptions = new() { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 1, + 1 + ), + ZIndex = int.MaxValue + }; + Task> creation = + resource.CreatePlacementAsync( createOptions ).AsTask(); + await transport.WaitForWriteCountAsync( 2 ); + Assert.False( creation.IsCompleted ); + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=999,p=1;OK\u001b\\" ) + ); + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=1001,p=2;OK\u001b\\" ) + ); + Assert.False( creation.IsCompleted ); + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=1001,p=1;OK\u001b\\" ) + ); + TerminalRasterPlacement placement = Assert.IsType( + ( await creation ).Value + ); + + Task malformedUpdate = placement.UpdateAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 1, + 1 + ), + ZIndex = int.MinValue + } + ).AsTask(); + await transport.WaitForWriteCountAsync( 3 ); + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=1001,p=1,p=1;OK\u001b\\" ) + ); + await Assert.ThrowsAsync( () => malformedUpdate ); + + Task missingUpdate = placement.UpdateAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 1, + 1 + ), + ZIndex = -7 + } + ).AsTask(); + await transport.WaitForWriteCountAsync( 4 ); + transport.Publish( + Encoding.ASCII.GetBytes( + "\u001b_Gi=1001,p=1;ENOENT:synthetic missing image\u001b\\" + ) + ); + TerminalControlMutationResult missingResult = await missingUpdate; + Assert.Equal( TerminalControlStatus.Unavailable, missingResult.Status ); + + int baselineWrites = transport.Writes.Count; + Assert.Equal( + TerminalControlStatus.Unavailable, + ( await placement.UpdateAsync( createOptions ) ).Status + ); + Assert.Equal( baselineWrites, transport.Writes.Count ); + await placement.DisposeAsync(); + await resource.DisposeAsync(); + Assert.Equal( baselineWrites, transport.Writes.Count ); + } + + [Fact] + public async Task AdvancedPlacementInvalidationAndStaleDisposalRemainLocalOnly() { + AcknowledgingTransport transport = new(); + await using TerminalSession session = await OpenSessionAsync( transport ); + TerminalRasterResource resource = await CreateResourceAsync( + session, + 4, + 3 + ); + TerminalControlResult creation = + await resource.CreatePlacementAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 1, + 1, + 3, + 2 + ), + Columns = 2, + Rows = 2, + ZIndex = 9 + } + ); + TerminalRasterPlacement placement = Assert.IsType( + creation.Value + ); + int baselineWrites = transport.Writes.Count; + + session.InvalidateState(); + TerminalControlMutationResult update = await placement.UpdateAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 4, + 3 + ), + ZIndex = -9 + } + ); + Assert.Equal( TerminalControlStatus.Unavailable, update.Status ); + Assert.Equal( baselineWrites, transport.Writes.Count ); + + await placement.DisposeAsync(); + await resource.DisposeAsync(); + Assert.Equal( baselineWrites, transport.Writes.Count ); + } + + private static TerminalRasterImage CreateImage( + int width, + int height + ) { + if ( 1 > width ) { + throw new ArgumentOutOfRangeException( nameof( width ) ); + } + if ( 1 > height ) { + throw new ArgumentOutOfRangeException( nameof( height ) ); + } + + return TerminalRasterImage.CreateRgb24( + width, + height, + new byte[ checked( width * height * 3 ) ] + ); + } + + private static async Task CreateResourceAsync( + TerminalSession session, + int width, + int height + ) { + ArgumentNullException.ThrowIfNull( session ); + TerminalControlResult result = + await session.CreateRasterResourceAsync( + CreateImage( + width, + height + ) + ); + Assert.Equal( TerminalControlStatus.Available, result.Status ); + return Assert.IsType( result.Value ); + } + + private static async ValueTask OpenSessionAsync( + AcknowledgingTransport transport + ) { + ArgumentNullException.ThrowIfNull( transport ); + TerminalSession session = await TerminalSession.OpenAsync( + new RecordingTerminalControlProvider(), + TerminalEndpoint.StandardInput, + TerminalEndpoint.StandardOutput, + transport, + transport, + new TerminalSessionOptions { + TerminalOverride = TerminalProfiles.Dumb, + ConfigureOutput = false, + MonotonicClock = new FrozenMonotonicClock(), + ObserveLifecycleEvents = false, + RequireInteractiveOutput = false + } + ); + session.RecordSemanticBackendEvidence( + TerminalProtocolBackend.ApcKittyGraphics, + TerminalCapabilitySupportState.Verified, + TerminalCapabilityEvidenceSource.ProtocolResponse + ); + return session; + } + + private sealed class AcknowledgingTransport : ITerminalInput, ITerminalOutput { + private readonly Channel input = Channel.CreateUnbounded( + new UnboundedChannelOptions { + SingleReader = true, + SingleWriter = false, + AllowSynchronousContinuations = false + } + ); + private readonly object synchronization = new(); + private readonly SemaphoreSlim writeSignal = new( 0 ); + private readonly List writes = []; + + internal bool AutoAcknowledgePlacements { + get; + init; + } = true; + + internal IReadOnlyList Writes { + get { + lock ( this.synchronization ) { + return this.writes.Select( + static value => value.ToArray() + ).ToArray(); + } + } + } + + public async ValueTask ReadAsync( + Memory buffer, + CancellationToken cancellationToken = default + ) { + byte[] value = await this.input.Reader.ReadAsync( + cancellationToken + ).ConfigureAwait( false ); + if ( value.Length > buffer.Length ) { + throw new InvalidOperationException( + "The scripted response exceeds the terminal input buffer." + ); + } + + value.AsSpan().CopyTo( buffer.Span ); + return value.Length; + } + + public ValueTask WriteAsync( + ReadOnlyMemory buffer, + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + lock ( this.synchronization ) { + this.writes.Add( buffer.ToArray() ); + } + this.writeSignal.Release(); + this.PublishAutomaticAcknowledgement( buffer.Span ); + return ValueTask.CompletedTask; + } + + public ValueTask FlushAsync( + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + return ValueTask.CompletedTask; + } + + internal void Publish( + byte[] value + ) { + ArgumentNullException.ThrowIfNull( value ); + if ( !this.input.Writer.TryWrite( value.ToArray() ) ) { + throw new InvalidOperationException( + "The scripted terminal input channel rejected a response." + ); + } + } + + internal async Task WaitForWriteCountAsync( + int count + ) { + if ( 0 > count ) { + throw new ArgumentOutOfRangeException( nameof( count ) ); + } + + using CancellationTokenSource timeout = new(); + timeout.CancelAfter( TimeSpan.FromSeconds( 5 ) ); + while ( this.Writes.Count < count ) { + await this.writeSignal.WaitAsync( + timeout.Token + ).ConfigureAwait( false ); + } + } + + private void PublishAutomaticAcknowledgement( + ReadOnlySpan frame + ) { + string text = Encoding.ASCII.GetString( frame ); + if ( text.StartsWith( + "\u001b_Ga=t,", + StringComparison.Ordinal + ) && TryReadIdentityField( + text, + ",I=", + out uint imageNumber + ) ) { + this.Publish( + Encoding.ASCII.GetBytes( + $"\u001b_Gi={1000u + imageNumber},I={imageNumber};OK\u001b\\" + ) + ); + return; + } + + if ( this.AutoAcknowledgePlacements + && text.StartsWith( + "\u001b_Ga=p,", + StringComparison.Ordinal + ) && TryReadIdentityField( + text, + ",i=", + out uint imageId + ) && TryReadIdentityField( + text, + ",p=", + out uint placementId + ) ) { + this.Publish( + Encoding.ASCII.GetBytes( + $"\u001b_Gi={imageId},p={placementId};OK\u001b\\" + ) + ); + } + } + + private static bool TryReadIdentityField( + string text, + string marker, + out uint value + ) { + ArgumentNullException.ThrowIfNull( text ); + ArgumentException.ThrowIfNullOrEmpty( marker ); + value = 0u; + + int start = text.IndexOf( + marker, + StringComparison.Ordinal + ); + if ( 0 > start ) { + return false; + } + start += marker.Length; + int end = start; + while ( end < text.Length + && text[ end ] is >= '0' and <= '9' ) { + ++end; + } + return start < end + && uint.TryParse( + text.AsSpan( + start, + end - start + ), + NumberStyles.None, + CultureInfo.InvariantCulture, + out value + ) + ; + } + } +} From 76138cae00db76c4ad5da55bfe01a889e7211a52 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 15:17:14 -0400 Subject: [PATCH 45/63] test: make advanced placement acknowledgement deterministic --- .../TerminalPersistentRasterAdvancedPlacementIntegrationTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedPlacementIntegrationTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedPlacementIntegrationTests.cs index da00d5356..1cc9f1c55 100644 --- a/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedPlacementIntegrationTests.cs +++ b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedPlacementIntegrationTests.cs @@ -124,6 +124,7 @@ ScriptedTransport transport new TerminalSessionOptions { TerminalOverride = TerminalProfiles.Dumb, ConfigureOutput = false, + MonotonicClock = new FrozenMonotonicClock(), ObserveLifecycleEvents = false, RequireInteractiveOutput = false } From 846c3e122d6a5357260c17c7a1a7623d03ca1b59 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 16:18:36 -0400 Subject: [PATCH 46/63] test: harden advanced raster ownership cycles --- ...ntRasterAdvancedOwnershipHardeningTests.cs | 512 ++++++++++++++++++ 1 file changed, 512 insertions(+) create mode 100644 tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedOwnershipHardeningTests.cs diff --git a/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedOwnershipHardeningTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedOwnershipHardeningTests.cs new file mode 100644 index 000000000..c2cb021d8 --- /dev/null +++ b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedOwnershipHardeningTests.cs @@ -0,0 +1,512 @@ +/* + Icod.Terminal.Tests + Automated test suite for the Icod.Terminal library. + Copyright (C) 2026 Timothy J. Bruce +*/ + +/* + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +namespace Icod.Terminal.Tests.Graphics; + +using System.Text; +using System.Threading.Channels; +using Icod.Terminal; +using Icod.TermInfo; +using Icod.Timing; +using Xunit; + +/// +/// Verifies T125 timeout ownership and repeated advanced-placement lifecycle hardening. +/// +public sealed class TerminalPersistentRasterAdvancedOwnershipHardeningTests { + private const int OwnershipCycleCount = 24; + + [Fact] + public async Task TimedOutAdvancedPlacementRetainsWireSlotUntilLateResponseIsConsumed() { + ManualMonotonicClock clock = new(); + AcknowledgingTransport transport = new() { + AutoAcknowledgePlacements = false + }; + await using TerminalSession session = await OpenSessionAsync( + transport, + clock + ); + TerminalRasterResource resource = await CreateResourceAsync( session ); + await using ( resource ) { + TerminalRasterPlacementOptions firstOptions = new() { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 1, + 1 + ), + Columns = 2, + Rows = 1, + ZIndex = -4 + }; + Task> first = + resource.CreatePlacementAsync( firstOptions ).AsTask(); + await transport.WaitForWriteCountAsync( 2 ); + await YieldSeveralTimesAsync(); + clock.Advance( TimeSpan.FromSeconds( 2 ) ); + + await Assert.ThrowsAsync( () => first ); + + TerminalRasterPlacementOptions secondOptions = new() { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 1, + 1 + ), + Columns = 1, + Rows = 2, + ZIndex = 5 + }; + Task> second = + resource.CreatePlacementAsync( secondOptions ).AsTask(); + await YieldSeveralTimesAsync(); + Assert.Equal( 2, transport.Writes.Count ); + + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=1001,p=1;OK\u001b\\" ) + ); + await transport.WaitForWriteCountAsync( 3 ); + Assert.Equal( + Encoding.ASCII.GetBytes( + "\u001b_Ga=p,i=1001,p=2,C=1,x=0,y=0,w=1,h=1,c=1,r=2,z=5\u001b\\" + ), + transport.Writes[ 2 ] + ); + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=1001,p=2;OK\u001b\\" ) + ); + + TerminalControlResult result = await second; + Assert.Equal( TerminalControlStatus.Available, result.Status ); + TerminalRasterPlacement placement = Assert.IsType( + result.Value + ); + await placement.DisposeAsync(); + } + } + + [Fact] + public async Task RepeatedAdvancedOwnershipCyclesRemainUsableAndIdempotent() { + ManualMonotonicClock clock = new(); + AcknowledgingTransport transport = new(); + await using TerminalSession session = await OpenSessionAsync( + transport, + clock + ); + + for ( int cycle = 0; cycle < OwnershipCycleCount; ++cycle ) { + TerminalRasterResource resource = await CreateResourceAsync( session ); + TerminalControlResult placementResult = + await resource.CreatePlacementAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 1, + 1 + ), + Columns = 3, + Rows = 2, + ZIndex = -cycle - 1 + } + ); + Assert.Equal( TerminalControlStatus.Available, placementResult.Status ); + TerminalRasterPlacement placement = Assert.IsType( + placementResult.Value + ); + + TerminalControlMutationResult update = await placement.UpdateAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 1, + 1 + ), + Columns = 2, + Rows = 1, + ZIndex = cycle + 1 + } + ); + Assert.True( update.Succeeded ); + + int cycleOffset = cycle * 5; + string createText = Encoding.ASCII.GetString( + transport.Writes[ cycleOffset + 1 ] + ); + string updateText = Encoding.ASCII.GetString( + transport.Writes[ cycleOffset + 2 ] + ); + Assert.Contains( ",x=0,y=0,w=1,h=1,c=3,r=2,z=", createText ); + Assert.Contains( ",x=0,y=0,w=1,h=1,c=2,r=1,z=", updateText ); + + await placement.DisposeAsync(); + int afterPlacementDispose = transport.Writes.Count; + await placement.DisposeAsync(); + Assert.Equal( afterPlacementDispose, transport.Writes.Count ); + + await resource.DisposeAsync(); + int afterResourceDispose = transport.Writes.Count; + await resource.DisposeAsync(); + Assert.Equal( afterResourceDispose, transport.Writes.Count ); + } + + Assert.Equal( + OwnershipCycleCount * 5, + transport.Writes.Count + ); + } + + private static TerminalRasterImage CreateSmallImage() { + return TerminalRasterImage.CreateRgb24( + 1, + 1, + [ 1, 2, 3 ] + ); + } + + private static async Task CreateResourceAsync( + TerminalSession session + ) { + ArgumentNullException.ThrowIfNull( session ); + TerminalControlResult result = + await session.CreateRasterResourceAsync( CreateSmallImage() ); + Assert.Equal( TerminalControlStatus.Available, result.Status ); + return Assert.IsType( result.Value ); + } + + private static async ValueTask OpenSessionAsync( + AcknowledgingTransport transport, + IMonotonicClock clock + ) { + ArgumentNullException.ThrowIfNull( transport ); + ArgumentNullException.ThrowIfNull( clock ); + TerminalSession session = await TerminalSession.OpenAsync( + new RecordingTerminalControlProvider(), + TerminalEndpoint.StandardInput, + TerminalEndpoint.StandardOutput, + transport, + transport, + new TerminalSessionOptions { + TerminalOverride = TerminalProfiles.Dumb, + ConfigureOutput = false, + MonotonicClock = clock, + ObserveLifecycleEvents = false, + RequireInteractiveOutput = false + } + ); + session.RecordSemanticBackendEvidence( + TerminalProtocolBackend.ApcKittyGraphics, + TerminalCapabilitySupportState.Verified, + TerminalCapabilityEvidenceSource.ProtocolResponse + ); + return session; + } + + private static async Task YieldSeveralTimesAsync() { + for ( int iteration = 0; iteration < 8; ++iteration ) { + await Task.Yield(); + } + } + + private sealed class AcknowledgingTransport : ITerminalInput, ITerminalOutput { + private readonly Channel input = Channel.CreateUnbounded( + new UnboundedChannelOptions { + SingleReader = true, + SingleWriter = false, + AllowSynchronousContinuations = false + } + ); + private readonly object synchronization = new(); + private readonly SemaphoreSlim writeSignal = new( 0 ); + private readonly List writes = []; + + internal bool AutoAcknowledgePlacements { + get; + set; + } = true; + + internal IReadOnlyList Writes { + get { + lock ( this.synchronization ) { + return this.writes.Select( + static value => value.ToArray() + ).ToArray(); + } + } + } + + public async ValueTask ReadAsync( + Memory buffer, + CancellationToken cancellationToken = default + ) { + byte[] value = await this.input.Reader.ReadAsync( + cancellationToken + ).ConfigureAwait( false ); + if ( value.Length > buffer.Length ) { + throw new InvalidOperationException( + "The scripted response exceeds the terminal input buffer." + ); + } + + value.AsSpan().CopyTo( buffer.Span ); + return value.Length; + } + + public ValueTask WriteAsync( + ReadOnlyMemory buffer, + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + lock ( this.synchronization ) { + this.writes.Add( buffer.ToArray() ); + } + this.writeSignal.Release(); + this.PublishAcknowledgement( buffer.Span ); + return ValueTask.CompletedTask; + } + + public ValueTask FlushAsync( + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + return ValueTask.CompletedTask; + } + + internal void Publish( + byte[] value + ) { + ArgumentNullException.ThrowIfNull( value ); + if ( !this.input.Writer.TryWrite( value.ToArray() ) ) { + throw new InvalidOperationException( + "The scripted terminal input channel rejected a response." + ); + } + } + + internal async Task WaitForWriteCountAsync( + int expected + ) { + if ( 0 > expected ) { + throw new ArgumentOutOfRangeException( nameof( expected ) ); + } + + using CancellationTokenSource timeout = new( + TimeSpan.FromSeconds( 5 ) + ); + while ( this.Writes.Count < expected ) { + await this.writeSignal.WaitAsync( + timeout.Token + ).ConfigureAwait( false ); + } + } + + private void PublishAcknowledgement( + ReadOnlySpan frame + ) { + string text = Encoding.ASCII.GetString( frame ); + if ( text.StartsWith( + "\u001b_Ga=t,", + StringComparison.Ordinal + ) && TryReadIdentityField( + text, + ",I=", + out uint imageNumber + ) ) { + this.Publish( + Encoding.ASCII.GetBytes( + $"\u001b_Gi={1000u + imageNumber},I={imageNumber};OK\u001b\\" + ) + ); + return; + } + + if ( !this.AutoAcknowledgePlacements ) { + return; + } + if ( text.StartsWith( + "\u001b_Ga=p,", + StringComparison.Ordinal + ) && TryReadIdentityField( + text, + ",i=", + out uint imageId + ) && TryReadIdentityField( + text, + ",p=", + out uint placementId + ) ) { + this.Publish( + Encoding.ASCII.GetBytes( + $"\u001b_Gi={imageId},p={placementId};OK\u001b\\" + ) + ); + } + } + + private static bool TryReadIdentityField( + string text, + string marker, + out uint value + ) { + ArgumentNullException.ThrowIfNull( text ); + ArgumentException.ThrowIfNullOrEmpty( marker ); + value = 0u; + + int start = text.IndexOf( + marker, + StringComparison.Ordinal + ); + if ( 0 > start ) { + return false; + } + start += marker.Length; + int end = start; + while ( end < text.Length + && text[ end ] is >= '0' and <= '9' ) { + ++end; + } + return start < end + && uint.TryParse( + text.AsSpan( + start, + end - start + ), + out value + ) + ; + } + } + + private sealed class ManualMonotonicClock : IMonotonicClock { + private readonly object synchronization = new(); + private readonly List waiters = []; + private long timestamp; + + public long GetTimestamp() { + lock ( this.synchronization ) { + return this.timestamp; + } + } + + public TimeSpan GetElapsedTime( + long startingTimestamp, + long endingTimestamp + ) { + return TimeSpan.FromTicks( + endingTimestamp - startingTimestamp + ); + } + + public ValueTask DelayAsync( + TimeSpan delay, + CancellationToken cancellationToken = default + ) { + if ( TimeSpan.Zero > delay ) { + throw new ArgumentOutOfRangeException( nameof( delay ) ); + } + cancellationToken.ThrowIfCancellationRequested(); + if ( TimeSpan.Zero == delay ) { + return ValueTask.CompletedTask; + } + + return new ValueTask( + this.DelayCoreAsync( + delay, + cancellationToken + ) + ); + } + + internal void Advance( + TimeSpan elapsed + ) { + if ( TimeSpan.Zero > elapsed ) { + throw new ArgumentOutOfRangeException( nameof( elapsed ) ); + } + + List due; + lock ( this.synchronization ) { + this.timestamp = checked( + this.timestamp + elapsed.Ticks + ); + due = this.waiters + .Where( + waiter => waiter.DueTimestamp <= this.timestamp + ).ToList(); + } + + foreach ( DelayWaiter waiter in due ) { + waiter.Completion.TrySetResult(); + } + } + + private async Task DelayCoreAsync( + TimeSpan delay, + CancellationToken cancellationToken + ) { + DelayWaiter waiter; + lock ( this.synchronization ) { + waiter = new DelayWaiter( + checked( this.timestamp + delay.Ticks ) + ); + this.waiters.Add( waiter ); + } + + using CancellationTokenRegistration registration = cancellationToken.Register( + static state => { + var tuple = (Tuple)state!; + tuple.Item1.TrySetCanceled( tuple.Item2 ); + }, + Tuple.Create( + waiter.Completion, + cancellationToken + ) + ); + + try { + await waiter.Completion.Task.ConfigureAwait( false ); + } finally { + lock ( this.synchronization ) { + this.waiters.Remove( waiter ); + } + } + } + + private sealed class DelayWaiter { + internal DelayWaiter( + long dueTimestamp + ) { + this.DueTimestamp = dueTimestamp; + } + + internal long DueTimestamp { + get; + } + + internal TaskCompletionSource Completion { + get; + } = new( + TaskCreationOptions.RunContinuationsAsynchronously + ); + } + } +} From 799d096fa439c43b4f31399a551c4524fe40fa10 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 16:21:02 -0400 Subject: [PATCH 47/63] test: correct advanced late-response ownership witness --- ...stentRasterAdvancedOwnershipHardeningTests.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedOwnershipHardeningTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedOwnershipHardeningTests.cs index c2cb021d8..a3d214517 100644 --- a/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedOwnershipHardeningTests.cs +++ b/tests/Icod.Terminal.Tests/src/Graphics/TerminalPersistentRasterAdvancedOwnershipHardeningTests.cs @@ -34,7 +34,7 @@ public sealed class TerminalPersistentRasterAdvancedOwnershipHardeningTests { private const int OwnershipCycleCount = 24; [Fact] - public async Task TimedOutAdvancedPlacementRetainsWireSlotUntilLateResponseIsConsumed() { + public async Task TimedOutAdvancedPlacementDoesNotAcceptLateResponseForLaterPlacement() { ManualMonotonicClock clock = new(); AcknowledgingTransport transport = new() { AutoAcknowledgePlacements = false @@ -77,12 +77,6 @@ public async Task TimedOutAdvancedPlacementRetainsWireSlotUntilLateResponseIsCon }; Task> second = resource.CreatePlacementAsync( secondOptions ).AsTask(); - await YieldSeveralTimesAsync(); - Assert.Equal( 2, transport.Writes.Count ); - - transport.Publish( - Encoding.ASCII.GetBytes( "\u001b_Gi=1001,p=1;OK\u001b\\" ) - ); await transport.WaitForWriteCountAsync( 3 ); Assert.Equal( Encoding.ASCII.GetBytes( @@ -90,10 +84,16 @@ public async Task TimedOutAdvancedPlacementRetainsWireSlotUntilLateResponseIsCon ), transport.Writes[ 2 ] ); + + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=1001,p=1;OK\u001b\\" ) + ); + await YieldSeveralTimesAsync(); + Assert.False( second.IsCompleted ); + transport.Publish( Encoding.ASCII.GetBytes( "\u001b_Gi=1001,p=2;OK\u001b\\" ) ); - TerminalControlResult result = await second; Assert.Equal( TerminalControlStatus.Available, result.Status ); TerminalRasterPlacement placement = Assert.IsType( From 7b38994c1ba936df5887d1aa015394c4ed626ddf Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 16:25:48 -0400 Subject: [PATCH 48/63] docs: demonstrate advanced persistent raster placement --- packaging/VerifyPersistentRasterPackage.ps1 | 10 ++++++- .../Program.cs | 29 ++++++++++++++----- samples/README.md | 12 ++++---- .../Program.cs | 22 +++++++++++++- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/packaging/VerifyPersistentRasterPackage.ps1 b/packaging/VerifyPersistentRasterPackage.ps1 index c96b51d71..bc020811e 100644 --- a/packaging/VerifyPersistentRasterPackage.ps1 +++ b/packaging/VerifyPersistentRasterPackage.ps1 @@ -37,9 +37,17 @@ if (-not (Test-Path -LiteralPath $packagePath -PathType Leaf)) { $requiredMembers = @( 'F:Icod.Terminal.TerminalCapability.PersistentRasterGraphics', + 'T:Icod.Terminal.TerminalRasterSourceRectangle', + 'M:Icod.Terminal.TerminalRasterSourceRectangle.#ctor(System.Int32,System.Int32,System.Int32,System.Int32)', + 'P:Icod.Terminal.TerminalRasterSourceRectangle.X', + 'P:Icod.Terminal.TerminalRasterSourceRectangle.Y', + 'P:Icod.Terminal.TerminalRasterSourceRectangle.Width', + 'P:Icod.Terminal.TerminalRasterSourceRectangle.Height', 'T:Icod.Terminal.TerminalRasterPlacementOptions', + 'P:Icod.Terminal.TerminalRasterPlacementOptions.SourceRectangle', 'P:Icod.Terminal.TerminalRasterPlacementOptions.Columns', 'P:Icod.Terminal.TerminalRasterPlacementOptions.Rows', + 'P:Icod.Terminal.TerminalRasterPlacementOptions.ZIndex', 'T:Icod.Terminal.TerminalRasterResource', 'M:Icod.Terminal.TerminalRasterResource.CreatePlacementAsync(Icod.Terminal.TerminalRasterPlacementOptions,System.Threading.CancellationToken)', 'M:Icod.Terminal.TerminalRasterResource.DisposeAsync', @@ -130,4 +138,4 @@ try { } } -Write-Host "1.11 persistent-raster package verification completed successfully for Icod.Terminal $ExpectedVersion ($Configuration)." +Write-Host "1.12 advanced persistent-raster package verification completed successfully for Icod.Terminal $ExpectedVersion ($Configuration)." diff --git a/samples/Icod.Terminal.PersistentRaster.Sample/Program.cs b/samples/Icod.Terminal.PersistentRaster.Sample/Program.cs index b734d0d53..fc8fa0d27 100644 --- a/samples/Icod.Terminal.PersistentRaster.Sample/Program.cs +++ b/samples/Icod.Terminal.PersistentRaster.Sample/Program.cs @@ -85,12 +85,18 @@ await session.WriteTextAsync( } await using TerminalRasterResource resource = resourceResult.Value; +TerminalRasterPlacementOptions placementOptions = new() { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 36, + 24 + ), + Columns = 24, + ZIndex = -1 +}; TerminalControlResult placementResult = - await resource.CreatePlacementAsync( - new TerminalRasterPlacementOptions { - Columns = 24 - } - ); + await resource.CreatePlacementAsync( placementOptions ); if ( TerminalControlStatus.Available != placementResult.Status || placementResult.Value is null ) { await session.WriteTextAsync( @@ -105,11 +111,18 @@ await session.WriteTextAsync( await using TerminalRasterPlacement placement = placementResult.Value; await session.WriteTextAsync( - "\r\nThe terminal-resident resource remains owned while its placement is resized below.\r\n" + "\r\nThe placement uses a source-pixel crop and relative z-order while the terminal-resident resource remains owned.\r\n" ); TerminalControlMutationResult update = await placement.UpdateAsync( new TerminalRasterPlacementOptions { - Columns = 16 + SourceRectangle = new TerminalRasterSourceRectangle( + 12, + 0, + 36, + 24 + ), + Columns = 16, + ZIndex = 1 } ); if ( !update.Succeeded ) { @@ -124,7 +137,7 @@ await session.WriteTextAsync( } await session.WriteTextAsync( - "\r\nPersistent placement update completed; disposal will release placement and resource ownership.\r\n" + "\r\nThe crop and relative stacking intent were updated; disposal will release placement and resource ownership.\r\n" ); return 0; diff --git a/samples/README.md b/samples/README.md index 0bfe56874..723d151ca 100644 --- a/samples/README.md +++ b/samples/README.md @@ -14,7 +14,7 @@ All samples target `net8.0`, `net9.0`, and `net10.0`. | Plan from semantic capability knowledge | `Icod.Terminal.CapabilityPlanning.Sample` | | Observe or temporarily own terminal colors | `Icod.Terminal.Color.Sample` | | Display a backend-neutral ephemeral raster | `Icod.Terminal.RasterGraphics.Sample` | -| Create/update/dispose terminal-resident raster ownership | `Icod.Terminal.PersistentRaster.Sample` | +| Create/update/dispose terminal-resident raster ownership with source crops and z-order | `Icod.Terminal.PersistentRaster.Sample` | | Plan persistent-raster lifecycle with TermInfo, optionally verify live support, then execute | `Icod.Terminal.TermInfoPersistentRaster.Sample` | | Own cursor style, synchronized output, progress, or pointer shape | focused state samples | | Publish title/location/prompt/shell metadata | focused metadata samples | @@ -91,7 +91,7 @@ The normal evidence-driven router may use verified Kitty Graphics or verified Si ### `Icod.Terminal.PersistentRaster.Sample` -Demonstrates the 1.11 persistent-raster ownership model using semantic APIs only. +Demonstrates the persistent-raster ownership model using semantic APIs only, including 1.12 source-pixel cropping and relative z-order. ```text dotnet run --project samples/Icod.Terminal.PersistentRaster.Sample/Icod.Terminal.PersistentRaster.Sample.csproj -f net10.0 @@ -102,11 +102,13 @@ The sample: 1. explicitly verifies `TerminalCapability.PersistentRasterGraphics`; 2. creates a `TerminalRasterImage` in memory; 3. creates an opaque `TerminalRasterResource`; -4. creates a placement with a cell-column extent; -5. updates the same placement at the current cursor; +4. creates a placement from a bounded source-pixel crop with a cell-column extent and nonzero z-order; +5. updates the same placement at the current cursor with a different crop, extent, and z-order; 6. uses `await using` so placement/resource cleanup is deterministic. -It does not mention Kitty, Sixel, image ids, image numbers, placement ids, or terminal brand. It also does not imply that resources are replayed after lifecycle invalidation. +`TerminalRasterSourceRectangle` coordinates are measured in source pixels and select which part of the owned raster resource participates in one placement. `ZIndex` expresses relative stacking intent. Neither option turns `Icod.Terminal` into a scene-layout engine: 1.12 still does not own relative placement graphs, screen-coordinate layout, or automatic composition policy. + +The sample does not mention Kitty, Sixel, image ids, image numbers, placement ids, or terminal brand. It also does not imply that resources are replayed after lifecycle invalidation. `packaging/VerifyPersistentRasterSample.ps1` enforces those backend-neutral source rules and builds the sample on every supported TFM. diff --git a/tools/package-persistent-raster-smoke/Program.cs b/tools/package-persistent-raster-smoke/Program.cs index a6b20606f..23b082daf 100644 --- a/tools/package-persistent-raster-smoke/Program.cs +++ b/tools/package-persistent-raster-smoke/Program.cs @@ -69,14 +69,34 @@ CancellationToken cancellationToken "PersistentRasterGraphics must retain the reviewed additive enum value 9." ); +TerminalRasterSourceRectangle rectangle = new( + 0, + 0, + 1, + 1 +); TerminalRasterPlacementOptions options = new() { + SourceRectangle = rectangle, Columns = 12, - Rows = 6 + Rows = 6, + ZIndex = -1 }; Require( 12 == options.Columns && 6 == options.Rows, "TerminalRasterPlacementOptions did not preserve caller-supplied cell extents." ); +Require( + options.SourceRectangle is TerminalRasterSourceRectangle storedRectangle + && 0 == storedRectangle.X + && 0 == storedRectangle.Y + && 1 == storedRectangle.Width + && 1 == storedRectangle.Height, + "TerminalRasterPlacementOptions did not preserve the caller-supplied source rectangle." +); +Require( + -1 == options.ZIndex, + "TerminalRasterPlacementOptions did not preserve caller-supplied z-order." +); Require( typeof( IAsyncDisposable ).IsAssignableFrom( typeof( TerminalRasterResource ) ), "TerminalRasterResource must remain asynchronously disposable." From 0b7961f6d8151253be57f65a69e17a12ec4bdec5 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 16:41:01 -0400 Subject: [PATCH 49/63] docs: prepare Icod.Terminal 1.12.0 stable candidate --- CHANGELOG.md | 27 +- Directory.Build.props | 2 +- Icod.Terminal-1.12.0-Development-Roadmap.md | 250 +++++----- Icod.Terminal-Development-Roadmap.md | 251 +++------- Icod.Terminal.csproj | 2 +- README.md | 48 +- docs/Architecture.md | 315 ++++--------- docs/Compatibility-and-Versioning.md | 491 +++++--------------- docs/Persistent-Raster-Ownership.md | 233 ++++++---- docs/Public-API-Baseline-1.12.md | 10 +- docs/Security-and-Privacy.md | 344 ++++---------- docs/releases/1.12.0.md | 141 ++++++ 12 files changed, 849 insertions(+), 1265 deletions(-) create mode 100644 docs/releases/1.12.0.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 49f6d5b80..5b71e1326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ 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.12.0 + +### Advanced persistent-raster placement geometry + +- Adds immutable `TerminalRasterSourceRectangle` with zero-based source-pixel `X` / `Y` and positive `Width` / `Height`. +- Adds `TerminalRasterPlacementOptions.SourceRectangle` and `.ZIndex` while preserving all existing placement options and public signatures. +- Validates source rectangles against immutable owning-resource dimensions before placement output; invalid create/update rectangles produce no new placement traffic. +- Accepts the complete signed `int` z-order domain and formats it deterministically using invariant signed decimal output. +- Routes source crop, cell extents, and z-order through the same acknowledged create/update placement transaction in deterministic `x,y,w,h,c,r,z` order. +- Preserves existing 1.11 placement bytes and behavior when the advanced options are omitted. + +### Ownership, hardening, and qualification + +- Keeps terminal image/placement identities opaque, placement position at the current cursor, generation-scoped ownership, child-before-resource cleanup, direct transfer, and the existing 256-resource / 4096-placement ceilings. +- Adds exact-edge crop, combined-option, invalid-no-output, `int.MinValue` / `int.MaxValue`, wrong-identity, malformed/duplicate-field, correlated `ENOENT`, timeout/late-response, generation-invalidation, stale-disposal, and repeated advanced ownership-cycle coverage. +- Table-drives internal `TerminalTermInfoSemanticEvidence` rules without changing public evidence semantics, routing behavior, or package dependencies. +- Extends the backend-neutral persistent-raster sample with source cropping and nonzero z-order while continuing to exclude protocol ids/backend branching. +- Extends fresh NuGet-only package consumption and generated XML-documentation checks for the new source-rectangle/z-order surface on `net8.0`, `net9.0`, and `net10.0`. +- Retains current `Icod.DCurses` downstream acceptance/hardening with no required downstream code change. +- Finalizes the 1.12 public API fingerprint as `eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8` while retaining all historical baselines unchanged. +- Preserves production dependencies at `Icod.TermInfo 1.11.0` and `Icod.Timing 1.0.0`. +- Continues to exclude automatic replay, Sixel persistent-resource emulation, public protocol ids, relative placement graphs, absolute screen-coordinate layout, Unicode placeholders, animation/frame lifecycle, image decoding/transcoding, and PTY/ConPTY hosting. + +See `docs/releases/1.12.0.md`, `docs/Persistent-Raster-Ownership.md`, `docs/Public-API-Baseline-1.12.md`, and `Icod.Terminal-1.12.0-Development-Roadmap.md` for the complete 1.12 contract. + ## 1.11.1 ### TermInfo persistent-raster integration contract @@ -169,4 +194,4 @@ See `docs/releases/1.8.0.md`, `docs/A180-APC-Construction-Contract-and-Reference ### Prior release history -Earlier changelog entries remain unchanged below this point in repository history. \ No newline at end of file +Earlier changelog entries remain unchanged below this point in repository history. diff --git a/Directory.Build.props b/Directory.Build.props index 710e41570..32b86906d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ 1.12.0 - alpha.1 + $(VersionPrefix) $(VersionPrefix)-$(VersionSuffix) $(Version) diff --git a/Icod.Terminal-1.12.0-Development-Roadmap.md b/Icod.Terminal-1.12.0-Development-Roadmap.md index 6fc87b0fa..ff6c42eb0 100644 --- a/Icod.Terminal-1.12.0-Development-Roadmap.md +++ b/Icod.Terminal-1.12.0-Development-Roadmap.md @@ -2,36 +2,36 @@ **Release:** `1.12.0` **Theme:** bounded advanced persistent-raster placement geometry -**Status:** T120 accepted; T121 implementation starting +**Status:** T120–T126 accepted; T127 stable release-candidate qualification pending **Stable compatibility floor:** `1.0.0` **Prior release:** published `1.11.1` ## Release objective -Version 1.12.0 extends the persistent-raster ownership model introduced in 1.11 with two narrowly bounded placement controls: +Version 1.12.0 extends the persistent-raster ownership model introduced in 1.11 with exactly two narrowly bounded placement controls: - pixel-space source rectangles; and - signed z-order. The release deliberately does not expand into relative placement graphs, Unicode placeholders, animation, cells/windows/layout, or other scene-graph responsibilities. -Before the new public surface lands, the release completes the already-approved table-driven cleanup of `TerminalTermInfoSemanticEvidence` so reviewed TermInfo evidence contracts are represented once and behavior remains easier to audit. +Before the new public surface landed, T121 completed the approved table-driven cleanup of `TerminalTermInfoSemanticEvidence` so reviewed TermInfo evidence contracts are represented once while preserving exact behavior. -The design authority is: +Design authority: [`docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md`](docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md) -The implementation plan is: +Implementation plan: [`docs/superpowers/plans/2026-09-12-1.12.0-advanced-raster-placement.md`](docs/superpowers/plans/2026-09-12-1.12.0-advanced-raster-placement.md) -The existing persistent ownership authority remains: +Permanent persistent-ownership authority: [`docs/Persistent-Raster-Ownership.md`](docs/Persistent-Raster-Ownership.md) -## Frozen public direction +## Frozen public surface -The additive 1.12 public surface is planned as: +The final 1.12 additive public API is: ```csharp public readonly struct TerminalRasterSourceRectangle { @@ -56,193 +56,172 @@ public sealed class TerminalRasterPlacementOptions { } ``` -Source rectangles are expressed in source-image pixels and must fit completely inside the resource. The public contract does not expose backend clipping behavior. +No public API was added after T123. -`ZIndex` accepts the full signed 32-bit `int` range. `null` retains the backend/default placement order. +Source rectangles use zero-based source-image pixels and must fit completely within the owning resource. `ZIndex` accepts the full signed `int` domain. `UpdateAsync(...)` remains a complete replacement of the placement at the current cursor position, not a partial patch against prior options. -`UpdateAsync(...)` remains a complete replacement of the placement at the current cursor position, not a partial patch against prior options. - -## Tranche roadmap +The final reviewed public API fingerprint is: ```text -T120 1.12 architecture/API regret gate + roadmap normalization accepted -T121 table-drive TerminalTermInfoSemanticEvidence in progress -T122 source-rectangle public contract + resource-aware validation planned -T123 z-order public contract + validation planned -T124 create/update encoder and acknowledged placement integration planned -T125 lifecycle/cancellation/malformed-response/boundary hardening planned -T126 sample/package-only consumer/XML docs/downstream qualification planned -T127 API freeze/release docs/three-OS/package release closure planned +eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8 ``` -## T120 — architecture/API regret gate - -Accepted. The design, implementation plan, current-roadmap normalization, and T120 authority record freeze source rectangle + z-order as the complete 1.12 feature scope. +## Tranche status -Acceptance: +```text +T120 architecture/API regret gate + roadmap normalization accepted +T121 table-drive TerminalTermInfoSemanticEvidence accepted +T122 source-rectangle public contract + resource-aware validation accepted +T123 z-order public contract + validation accepted +T124 create/update encoder and acknowledged placement integration accepted +T125 lifecycle/adversarial/boundary hardening accepted +T126 sample/package-only consumer/XML docs/downstream qualification accepted +T127 API freeze/release docs/three-OS/package release closure in progress +``` -- published `1.11.1` is the explicit base; -- source rectangle and z-order are the only new placement features approved for this release; -- relative placement, Unicode placeholders, animation, and scene-graph ownership remain excluded; -- no new `TerminalCapability` value is planned; -- no production dependency change is planned; -- stable compatibility floor remains `1.0.0`; -- the current long-range roadmap is normalized from stale 1.11.0 wording to published 1.11.1 and this 1.12 line. +## Accepted checkpoints -See [`docs/T120-1.12.0-Architecture-and-API-Regret-Gate.md`](docs/T120-1.12.0-Architecture-and-API-Regret-Gate.md). +### T120–T123 -## T121 — table-driven TermInfo semantic evidence +The design/API-regret gate, behavior-preserving TermInfo evidence cleanup, source-rectangle contract/resource-aware validation, z-order contract, and final public API freeze are accepted. The only intended public additions are the eight members recorded in `docs/Public-API-Baseline-1.12.md`. -Refactor the internal `TerminalTermInfoSemanticEvidence` implementation into reviewed immutable tables while preserving exact behavior. +### T124 — placement integration -The exact semantic contracts remain: +Accepted on exact head: ```text -ClipboardWrite <- extended string Ms -CursorStyle <- extended string Ss -PaletteColor <- can_change_color + initialize_color +aba1f7c0989d2c451294edf75590637c227a7e05 ``` -The metadata-backed input backend advertisements remain: +Workflow: ```text -CsiFocusReporting <- fe + fd + kxIN + kxOUT -CsiBracketedPaste <- BE + BD + PS + PE -CsiMouseReporting <- XM + xm + key_mouse prefix validation +#1653 / 34713178166 ``` -Acceptance: +All nine PR jobs passed. -- `Seed(...)` and `HasExactImplementation(...)` share one exact-semantic rule table; -- backend advertisements are represented by one reviewed backend rule table; -- all existing evidence states, subjects, sources, routing outcomes, and validation behavior remain unchanged; -- no public API or package dependency change; -- focused routing tests and full Staging matrix remain green. +The accepted backend-neutral semantic contract is implemented by one shared acknowledged placement path. The reviewed wire order is: -## T122 — source rectangle contract +```text +Ga=p,i=,p=,C=1[,x=...[,y=...[,w=...[,h=...]]]][,c=...][,r=...][,z=...] +``` -Add `TerminalRasterSourceRectangle` and `TerminalRasterPlacementOptions.SourceRectangle`. +A present source rectangle emits all four crop fields together. Signed z-order uses invariant decimal formatting. Existing 1.11 placement bytes remain unchanged when the advanced options are omitted. -Acceptance: +### T125 — hardening -- zero-based non-negative `X`/`Y`; -- positive `Width`/`Height`; -- scalar bounds respect `TerminalRasterImage.MaximumDimension`; -- resource state retains immutable source width/height metadata only; -- create/update reject a rectangle extending beyond the actual source resource before output; -- `null` means full source image; -- no raster-pixel cache or replay behavior is introduced; -- focused constructor/options/resource-validation tests cover boundaries and no-output failures. +Accepted on exact head: -## T123 — z-order contract +```text +799d096fa439c43b4f31399a551c4524fe40fa10 +``` -Add `TerminalRasterPlacementOptions.ZIndex`. +Workflow: -Acceptance: +```text +#1657 / 34716833678 +``` -- nullable signed 32-bit `int` surface; -- full `int.MinValue..int.MaxValue` accepted; -- negative values preserved; -- `null` means backend/default order; -- no new capability enum or backend selector; -- source rectangle and z-order remain orthogonal options. +All nine PR jobs passed. -## T124 — persistent placement integration +Acceptance covers: -Extend the existing acknowledged placement create/update path. +- source rectangles ending exactly at the source right/bottom edge; +- combined rectangle + Columns + Rows + signed z-order; +- invalid create/update rectangles rejected before new output; +- `int.MinValue` and `int.MaxValue` through real acknowledged placement operations; +- wrong identity, duplicate-field malformed response, correlated `ENOENT`, timeout, and late-response ownership; +- generation invalidation with controlled `Unavailable` and stale local-only disposal; +- 24 repeated advanced create/place/update/delete ownership cycles; +- unchanged 256-resource / 4096-placement ceilings and registry behavior. -The reviewed backend emits: +The Windows scheduler-sensitive scripted placement witness was made deterministic with the existing frozen monotonic clock; production timeout semantics were not changed. -```text -x= -y= -w= -h= -z= -``` +### T126 — sample/package/downstream qualification -alongside existing private image/placement identities, `C=1`, and optional cell extents. +Accepted on exact head: -Acceptance: +```text +7b38994c1ba936df5887d1aa015394c4ed626ddf +``` -- all four source rectangle fields are emitted together; -- `z` uses invariant signed decimal formatting; -- create and update use the same encoder contract; -- existing placement acknowledgement correlation is unchanged; -- update retains private placement identity and current-cursor replacement semantics; -- omitted advanced options preserve 1.11 bytes/behavior. +Workflow: -## T125 — hardening +```text +#1658 / 34717103814 +``` -Qualify the new geometry through existing lifecycle and query ownership. +All nine PR jobs passed. Acceptance includes: -- source rectangle exact-edge boundaries; -- invalid rectangle before output; -- min/max z-order; -- create/update with combinations of rectangle, cell extents, and z-order; -- caller cancellation before commitment; -- transport failure after commitment; -- malformed/wrong-identity/negative acknowledgement behavior; -- `ENOENT` invalidation; -- generation invalidation/stale local-only cleanup; -- resource/placement capacity ceilings unchanged; -- repeated replacement/disposal cycles. - -## T126 — consumer and downstream qualification +- backend-neutral `Icod.Terminal.PersistentRaster.Sample` source cropping and z-order create/update usage; +- sample documentation explaining source-pixel crops and relative stacking intent without scene-layout claims; +- fresh NuGet-only consumption of `TerminalRasterSourceRectangle`, `SourceRectangle`, and `ZIndex` on `net8.0`, `net9.0`, and `net10.0`; +- generated XML documentation checks for the new type, constructor, four properties, and both new placement-option members on all package TFMs; +- current Stable 1.x downstream `Icod.DCurses` acceptance/hardening soak with no downstream code change. -Update or add executable sample coverage that demonstrates cropping and layering without exposing backend ids or commands. +## T127 — stable release closure -Extend fresh package-only consumer validation for: +The stable release candidate must: -- `TerminalRasterSourceRectangle`; -- `SourceRectangle`; -- `ZIndex`; -- placement create/update on `net8.0`, `net9.0`, `net10.0`. +1. keep the public API fingerprint exactly `eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8`; +2. set package version metadata to stable `1.12.0`; +3. synchronize README, changelog, release notes, current roadmap, architecture, persistent ownership, security/privacy, and compatibility authorities; +4. preserve production dependencies exactly: -Require generated XML documentation and current `Icod.DCurses` package acceptance/soak. +```text +Icod.TermInfo 1.11.0 +Icod.Timing 1.0.0 +``` -## T127 — stable release closure +5. pass the exact-head full Staging matrix: -Before stable release: +```text +Runtime Windows +Runtime Linux +Runtime macOS +Package candidate / public API freeze +Package Foundation +Package Presentation +Package Semantic and hardening +Package Stable 1.x release line +Validated package artifact +``` -- freeze the final 1.12 public API baseline and fingerprint; -- update README, changelog, package release notes, compatibility/security/architecture/persistent-raster authorities, sample catalog, and curated `docs/releases/1.12.0.md`; -- qualify exact head on Runtime Windows/Linux/macOS; -- pass package candidate/API freeze; -- pass Package Foundation, Presentation, Semantic and hardening, Stable 1.x release line; -- produce validated package artifact; -- leave merge, mainline Release validation, tag, and NuGet publication to the maintainer/release workflow. +6. record the accepted candidate SHA/workflow/fingerprint/dependency/downstream evidence in a final closure document; +7. run the same full matrix once more after closure-only status documentation; +8. leave merge, mainline Release validation, `v1.12.0` tagging, GitHub Release creation, and NuGet publication to the maintainer/release workflow. ## Compatibility guardrails -Version 1.12 must preserve: +Version 1.12 preserves: -- existing public signatures and enum numeric values; -- existing 1.11 behavior when the new placement options are unused; +- every pre-existing public signature and enum numeric value; +- 1.11 placement bytes/behavior when the new options are unused; - one authoritative input/query path; - opaque terminal resource/placement identities; - generation-scoped persistent ownership; - no hidden source-raster retention or replay; - resource ceiling `256` and placement ceiling `4096`; - direct-transfer persistent transport only; -- production dependencies `Icod.TermInfo 1.11.0` and `Icod.Timing 1.0.0` unless a separately reviewed requirement changes them. +- production dependencies `Icod.TermInfo 1.11.0` and `Icod.Timing 1.0.0`. ## Explicit exclusions Version 1.12.0 does not include: -- relative placements; -- parent image/placement identities; -- relative horizontal/vertical offsets; +- relative placements or parent placement identities; - placement chains, cycle detection, or graph lifetime ownership; - Unicode placeholder/virtual placements; - animation/frame lifecycle; -- pixel offsets inside terminal cells beyond source cropping; +- absolute screen-coordinate placement or pixel offsets inside terminal cells beyond source cropping; - caller-selected raster backend; - public Kitty protocol identities; - generic raw Kitty command dispatch; +- automatic replay/re-upload after invalidation; +- retained source-image caches; - image-file decoding/transcoding; - PTY/ConPTY process hosting; - cells/windows/damage/layout policy belonging to `Icod.DCurses`. @@ -250,11 +229,16 @@ Version 1.12.0 does not include: ## Release gate ```text -approved design - -> T121 behavior-preserving internal cleanup - -> T122/T123 additive public value/options contracts - -> T124 acknowledged backend integration - -> T125 adversarial/lifecycle hardening - -> T126 package/downstream/sample qualification - -> T127 exact-head stable release closure +T120 accepted + -> T121 accepted + -> T122 accepted + -> T123 accepted / API frozen + -> T124 accepted + -> T125 accepted + -> T126 accepted + -> T127 stable candidate + -> exact-head matrix + -> closure-only record + -> final exact-head matrix + -> maintainer handoff ``` diff --git a/Icod.Terminal-Development-Roadmap.md b/Icod.Terminal-Development-Roadmap.md index 1149a6aa2..89c7a3100 100644 --- a/Icod.Terminal-Development-Roadmap.md +++ b/Icod.Terminal-Development-Roadmap.md @@ -4,8 +4,7 @@ - **Package:** `Icod.Terminal` - **Language:** C# 13 - **Target frameworks:** `net8.0`; `net9.0`; `net10.0` -- **Current stable release:** `1.11.1` -- **Next development line:** `1.12.0` — bounded advanced persistent-raster placement geometry +- **Current stable candidate:** `1.12.0` — bounded advanced persistent-raster placement geometry - **Stable compatibility floor:** `1.0.0` ## Purpose @@ -36,227 +35,119 @@ terminal applications - `Icod.DCurses` owns cells, windows, virtual-screen state, layout, refresh/diff policy, damage, and higher-level curses presentation abstractions. - PTY/process hosting remains orthogonal to the `Icod.Terminal` runtime contract. -## Published 1.8 result - -The 1.5–1.8 program established normalized control families, complete CSI grammar/pixel geometry, backend-neutral raster images, Sixel, and Kitty Graphics routing. +## Published release sequence through 1.11.1 ```text -1.5.0 normalized control families / capability evidence / semantic routing -1.6.0 complete CSI grammar / terminal and cell pixel geometry -1.7.0 DCS / Sixel / public backend-neutral raster contract -1.8.0 APC / Kitty Graphics / verified multi-backend raster routing -1.8.1 documentation and sample maintenance +1.5.0 normalized control families / capability evidence / semantic routing +1.6.0 complete CSI grammar / terminal and cell pixel geometry +1.7.0 DCS / Sixel / public backend-neutral raster contract +1.8.0 APC / Kitty Graphics / verified multi-backend raster routing +1.8.1 documentation and sample maintenance +1.9.0 unsolicited protocol-neutral semantic events +1.10.0 semantic capability inspection and explicit bounded verification +1.11.0 opaque persistent raster resources and placements +1.11.1 TermInfo persistent-raster lifecycle integration contract ``` -The 1.7/1.8 raster fingerprint is: +Final 1.11 public API fingerprint, retained by 1.11.1: ```text -847441fb4a8cdc89979aca9e96178f939895b93ec19a973232210af09716f700 +9336a1f6def1c4b02e86db813bae27f45b95af33f47a2cf10dccd4d1d44324f2 ``` -## Published 1.9 result +## 1.12.0 stable candidate -Version 1.9 established unsolicited semantic-event ownership through the unified session event stream: +Version 1.12 deliberately extends the existing opaque placement object with only: ```text -active query response - -> recognized unsolicited semantic event - -> ordinary application input +pixel-space source rectangles +signed z-order ``` -It added the protocol-neutral semantic event envelope and interactive notification reporting while retaining one authoritative input reader. +It does not add a scene graph, relative placement chain, Unicode placeholder model, animation/frame lifecycle, absolute screen-coordinate placement, public backend selector, or replay cache. -Final 1.9 fingerprint: +The accepted sequence is: ```text -e652e6fd65cd43422ca84b7c4c2a1815ee7ead9b2a64285e0e17cf39614b0315 +T120 architecture/API regret gate + roadmap normalization accepted +T121 table-driven TermInfo semantic evidence accepted +T122 source-rectangle public contract + resource-aware validation accepted +T123 z-order public contract accepted +T124 acknowledged create/update encoder integration accepted +T125 lifecycle/adversarial/boundary hardening accepted +T126 sample/package/downstream qualification accepted +T127 API freeze and stable release closure in progress ``` -## Published 1.10 result - -Version 1.10 added protocol-neutral semantic capability inspection and explicit bounded verification: +The public 1.12 additions are exactly: ```text -inspect - side-effect free - reports what the session currently knows - -verify - explicit and bounded - strengthens knowledge only through reviewed live probes +TerminalRasterSourceRectangle +TerminalRasterSourceRectangle..ctor(int,int,int,int) +TerminalRasterSourceRectangle.X +TerminalRasterSourceRectangle.Y +TerminalRasterSourceRectangle.Width +TerminalRasterSourceRectangle.Height +TerminalRasterPlacementOptions.SourceRectangle +TerminalRasterPlacementOptions.ZIndex ``` -It also froze loose dependency coupling: `Icod.Terminal.csproj` remains the direct dependency authority, while restore/build is the compatibility witness for the package graph. - -Final 1.10 fingerprint: +Final 1.12 public API fingerprint: ```text -ee705250d19d51df92645e5020f188646dd2dbf38483278e6e57ce6fbbc1e9fb +eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8 ``` -## Published 1.11 result - -Version 1.11 established a separate persistent raster ownership domain above the existing `TerminalRasterImage` and capability-planning contracts. - -The stable semantic flow is: +Accepted implementation/qualification checkpoints: ```text -PersistentRasterGraphics verification - -> CreateRasterResourceAsync(...) - -> TerminalRasterResource - -> CreatePlacementAsync(...) - -> TerminalRasterPlacement - -> UpdateAsync(...) - -> DisposeAsync() - -> DisposeAsync() +T124 aba1f7c0989d2c451294edf75590637c227a7e05 #1653 / 34713178166 +T125 799d096fa439c43b4f31399a551c4524fe40fa10 #1657 / 34716833678 +T126 7b38994c1ba936df5887d1aa015394c4ed626ddf #1658 / 34717103814 ``` -The public model remains opaque. Kitty image ids, image numbers, placement ids, raw APC command dictionaries, and backend selection stay internal. +Each listed workflow passed the full nine-job PR matrix. -Final 1.11 public API fingerprint: +The production dependency graph remains: ```text -9336a1f6def1c4b02e86db813bae27f45b95af33f47a2cf10dccd4d1d44324f2 +Icod.TermInfo 1.11.0 +Icod.Timing 1.0.0 ``` -Detailed 1.11 authorities: +The versioned roadmap and release authorities are: -- [`Icod.Terminal-1.11.0-Development-Roadmap.md`](Icod.Terminal-1.11.0-Development-Roadmap.md) -- [`docs/releases/1.11.0.md`](docs/releases/1.11.0.md) +- [`Icod.Terminal-1.12.0-Development-Roadmap.md`](Icod.Terminal-1.12.0-Development-Roadmap.md) +- [`docs/releases/1.12.0.md`](docs/releases/1.12.0.md) - [`docs/Persistent-Raster-Ownership.md`](docs/Persistent-Raster-Ownership.md) -- [`docs/Public-API-Baseline-1.11.md`](docs/Public-API-Baseline-1.11.md) - -## Published 1.11.1 integration patch - -Version 1.11.1 kept the 1.11 public API and persistent-raster runtime semantics unchanged while proving the intended loose-coupling integration with `Icod.TermInfo.Inspection 1.11.0`. - -The accepted consumer flow is: - -```text -TermInfo static inspection / classification / planning - -> optional Terminal live verification - -> caller-owned Verified lifecycle evidence - -> TermInfo reclassification / replanning - -> Terminal runtime resource / placement execution -``` - -Inspection remains a consumer/test/sample-only dependency. The production package graph remains `Icod.TermInfo 1.11.0` plus `Icod.Timing 1.0.0`. - -Detailed 1.11.1 authorities: - -- [`Icod.Terminal-1.11.1-Development-Roadmap.md`](Icod.Terminal-1.11.1-Development-Roadmap.md) -- [`docs/releases/1.11.1.md`](docs/releases/1.11.1.md) -- [`docs/T1111-E-1.11.1-Release-Closure.md`](docs/T1111-E-1.11.1-Release-Closure.md) - -## Current development line: 1.12.0 - -Version 1.12 is intentionally bounded to advanced placement geometry that extends the existing opaque placement object without adding a placement graph or virtual-screen ownership. - -Approved additions: - -```text -pixel-space source rectangles -signed z-order -``` - -Before those public additions, T121 performs the behavior-preserving table-driven cleanup of `TerminalTermInfoSemanticEvidence` approved at 1.11.1 closure. - -The 1.12 sequence is: - -```text -T120 architecture/API regret gate + roadmap normalization -T121 table-driven TermInfo semantic evidence -T122 source-rectangle public contract + resource-aware validation -T123 z-order public contract -T124 acknowledged create/update encoder integration -T125 lifecycle/adversarial/boundary hardening -T126 sample/package/downstream qualification -T127 API freeze and stable release closure -``` - -The versioned roadmap is: - -[`Icod.Terminal-1.12.0-Development-Roadmap.md`](Icod.Terminal-1.12.0-Development-Roadmap.md) - -The design authority is: - -[`docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md`](docs/superpowers/specs/2026-09-12-1.12.0-advanced-raster-placement-design.md) - -### 1.12 boundaries - -Source rectangles are zero-based source-image pixel rectangles and must fit completely inside the uploaded raster. The library rejects invalid rectangles before output rather than exposing backend-specific clipping behavior. - -Z-order is a nullable signed 32-bit placement property. The existing current-cursor/no-cursor-movement placement semantics remain intact. +- [`docs/Public-API-Baseline-1.12.md`](docs/Public-API-Baseline-1.12.md) -The following remain deferred beyond 1.12: +## Stable architecture guardrails -- relative placements and parent identities; -- placement chains, cycles, and depth limits; -- Unicode placeholder/virtual placements; -- animation/frame lifecycle; -- scene/window/cell/layout ownership; -- source-image caches/replay; -- caller-selected raster backends and raw Kitty dispatch. +The 1.x line continues to preserve: -## Parallel evidence tracks +- one authoritative input/query/event path per live session; +- semantic capability planning rather than terminal-brand guessing; +- bounded parsers, queries, semantic events, raster work, and persistent registries; +- backend-neutral public raster semantics; +- opaque persistent resource/placement identities; +- current-cursor placement rather than a Terminal-owned layout engine; +- generation-scoped persistent ownership with no automatic replay; +- deterministic cleanup and committed-output integrity; +- production package dependencies declared centrally by `Icod.Terminal.csproj`. -### Graphics performance +## Long-range directions -Kitty direct transfer remains the portability/security default. Compression or alternate transport should be adopted only after benchmark evidence across representative icons, diagrams, screenshots, gradients, photographs, and high-entropy rasters. +Future development may consider only independently justified, separately reviewed tracks such as: -Measure wire bytes, CPU time, allocations, first-frame latency, and total transfer latency. File, temporary-file, and shared-memory transports remain excluded unless direct-transfer measurements demonstrate a concrete problem that justifies their filesystem/IPC complexity. +- richer terminal observations where the wire contract is sufficiently portable; +- additional semantic capability planning where truthful evidence exists; +- additional bounded output/state semantics with deterministic cleanup; +- higher-level integration needed by `Icod.DCurses` without moving cells/windows/layout into `Icod.Terminal`; +- PTY/ConPTY integration through the separate `Icod.Pty` project rather than by expanding this package’s runtime ownership. -### Diagnostics and observability +Advanced raster features beyond 1.12—relative placement graphs, Unicode placeholders, animation, frame lifecycle, or scene ownership—remain future design questions rather than implied extensions of source cropping/z-order. -Future diagnostic surfaces may explain semantic operations, support-state changes, probe lifecycle, backend selection, lifecycle generations, restoration failures, and committed graphics failures. - -They must not expose keyboard text, paste contents, clipboard data, notification contents, hyperlinks, shell command lines, or raster payload bytes by default, and must preserve the protocol-neutral public planning model. - -## Long-range architectural guardrails - -The stable 1.x program preserves these boundaries: - -- no process-global current terminal; -- no second live input reader; -- no raw control-family dispatcher as the ordinary extension mechanism; -- no generic vendor-event/raw-frame stream as the ordinary semantic model; -- no support selection based solely on terminal brand, `TERM`, host OS, or environment variables; -- no tests/verifiers that duplicate exact transitive dependency pins merely to restate package metadata; -- no PTY/ConPTY process hosting in `Icod.Terminal`; -- no cells/windows/damage/layout/widget ownership that belongs in `Icod.DCurses`; -- no image-file decoding/transcoding requirement in the core terminal package; -- no hidden replay of persistent terminal state without a separately reviewed contract; -- no unbounded terminal-controlled input, event buffering, query state, graphics state, resource registry, or placement registry; -- no public persistent-graphics abstraction exposing backend-specific numeric identifiers as its common identity model. - -## Sequencing rationale - -```text -event ownership completed in 1.9 - -> capability visibility completed in 1.10 - -> persistent ownership completed in 1.11 - -> loose lifecycle planning integration completed in 1.11.1 - -> bounded advanced placement geometry in 1.12 -``` - -`Icod.DCurses` remains the primary downstream witness for richer presentation needs. It should consume `Icod.Terminal` semantic resource/placement ownership rather than force the terminal layer to absorb virtual-screen or scene-graph responsibilities. - -## Permanent 1.x authorities - -Current contract authorities include: - -- [`docs/Architecture.md`](docs/Architecture.md) -- [`docs/Terminal-Session-and-Ownership.md`](docs/Terminal-Session-and-Ownership.md) -- [`docs/Lifecycle-and-Restoration.md`](docs/Lifecycle-and-Restoration.md) -- [`docs/Input-and-Events.md`](docs/Input-and-Events.md) -- [`docs/Queries-and-Responses.md`](docs/Queries-and-Responses.md) -- [`docs/Presentation-and-Reversible-State.md`](docs/Presentation-and-Reversible-State.md) -- [`docs/Semantic-Output-Protocols.md`](docs/Semantic-Output-Protocols.md) -- [`docs/Control-Language-Normalization-and-Graphics-Roadmap.md`](docs/Control-Language-Normalization-and-Graphics-Roadmap.md) -- [`docs/Capability-Inspection-and-Planning.md`](docs/Capability-Inspection-and-Planning.md) -- [`docs/Persistent-Raster-Ownership.md`](docs/Persistent-Raster-Ownership.md) -- [`docs/Security-and-Privacy.md`](docs/Security-and-Privacy.md) -- [`docs/Compatibility-and-Versioning.md`](docs/Compatibility-and-Versioning.md) -- [`samples/README.md`](samples/README.md) +## Maintainer handoff rule -Versioned roadmaps and tranche documents remain historical design evidence and should not be rewritten merely to make their pre-release status language look current after publication. +For the 1.12 stable candidate, merge, mainline Release validation, `v1.12.0` tagging, GitHub Release creation, and NuGet publication remain maintainer/release-workflow actions after PR exact-head qualification and closure are complete. diff --git a/Icod.Terminal.csproj b/Icod.Terminal.csproj index 0820ade9d..4d65e6ac2 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.12.0-alpha.1 begins bounded advanced persistent-raster placement development. The line first table-drives existing TermInfo semantic-evidence reconciliation with behavior parity, then adds source rectangles and signed z-order to the opaque placement options surface without relative-placement graphs, Unicode placeholders, animation, new capability values, or new production dependencies. Development roadmap: https://github.com/uniblab/Icod.Terminal/blob/feature/1.12.0-advanced-raster-placement/Icod.Terminal-1.12.0-Development-Roadmap.md + Icod.Terminal 1.12.0 adds bounded source-pixel rectangles and signed z-order to persistent raster placement while preserving opaque identities, acknowledged create/update, generation-scoped ownership, bounded cleanup, and the existing production dependency graph. Release notes: https://github.com/uniblab/Icod.Terminal/blob/main/docs/releases/1.12.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 diff --git a/README.md b/README.md index 2ca7a61e2..590fc0aa5 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,14 @@ ## Status -`1.11.1` is the current stable patch line. It preserves the 1.11.0 backend-neutral persistent terminal-resident raster API while adding executable documentation and deterministic contract tests for loose coupling with `Icod.TermInfo.Inspection 1.11.0` lifecycle planning. +`1.12.0` is the current stable line. It extends the backend-neutral persistent terminal-resident raster placement API with bounded source-pixel rectangles and signed z-order while preserving opaque identities, acknowledged create/update, generation-scoped ownership, deterministic cleanup, and the existing production dependency graph. -The stable `1.0.0` compatibility floor remains unchanged. `Icod.Terminal` continues to preserve one authoritative live input path, bounded query/protocol handling, lifecycle-aware ownership, deterministic cleanup, and protocol-neutral public planning surfaces. Version 1.11.1 adds no production public API and does not add Inspection or Source to the production dependency graph. +The stable `1.0.0` compatibility floor remains unchanged. `Icod.Terminal` continues to preserve one authoritative live input path, bounded query/protocol handling, lifecycle-aware ownership, deterministic cleanup, and protocol-neutral public planning surfaces. Version 1.12 adds no public backend selector, scene graph, automatic replay, or production dependency. ## Installation ```text -dotnet add package Icod.Terminal --version 1.11.1 +dotnet add package Icod.Terminal --version 1.12.0 ``` The package targets: @@ -132,7 +132,7 @@ Supported public storage forms are `Rgb24`, `Rgba32`, and `Indexed8` with an RGB ### Persistent resources and placements -Version 1.11 adds a separate ownership model for terminal-resident raster data: +Version 1.11 established a separate ownership model for terminal-resident raster data; version 1.12 adds bounded source-pixel cropping and relative z-order to the existing placement options: ```csharp TerminalCapabilityStatus capability = await session.VerifyCapabilityAsync( @@ -149,7 +149,14 @@ if ( capability.IsUsable ) { TerminalControlResult placementResult = await resource.CreatePlacementAsync( new TerminalRasterPlacementOptions { - Columns = 24 + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 1, + 1 + ), + Columns = 24, + ZIndex = -1 } ); @@ -158,7 +165,14 @@ if ( capability.IsUsable ) { TerminalControlMutationResult update = await placement.UpdateAsync( new TerminalRasterPlacementOptions { - Columns = 16 + SourceRectangle = new TerminalRasterSourceRectangle( + 1, + 0, + 1, + 1 + ), + Columns = 16, + ZIndex = 1 } ); } @@ -166,9 +180,9 @@ if ( capability.IsUsable ) { Resource and placement identity is opaque. The public API does not expose Kitty image ids, image numbers, placement ids, raw APC commands, or backend selection. -`Columns` and `Rows` are independently optional and each supplied value is bounded to `1..16384`. Placement uses the terminal's current cursor location and does not move the text cursor. To reposition a placement, move the cursor through ordinary terminal semantics and call `UpdateAsync(...)`. +`Columns` and `Rows` are independently optional and each supplied value is bounded to `1..16384`. `SourceRectangle` is measured in source-image pixels, must fit completely within the resource, and is validated before placement output. `ZIndex` is nullable and accepts the full signed 32-bit range. Placement still uses the terminal's current cursor location and does not move the text cursor; source cropping and z-order do not create an absolute layout or scene-graph contract. -Persistent identities are session-generation scoped. Explicit invalidation and lifecycle generation changes stale existing handles. Version 1.11 does not retain hidden raster copies for automatic replay or re-upload after suspend/resume uncertainty. +Persistent identities are session-generation scoped. Explicit invalidation and lifecycle generation changes stale existing handles. Version 1.12 does not retain hidden raster copies for automatic replay or re-upload after suspend/resume uncertainty. The library bounds live ownership to 256 persistent resources and 4096 placements per session. Current cleanup deletes placements before resource data; stale cleanup is local-only and never emits stale numeric protocol identities. @@ -190,7 +204,7 @@ session.Terminal The consumer-owned evidence bridge promotes only conclusive live observations. `Unknown`, `Advertised`, unrelated capabilities, and endpoint unavailability remain distinct and are not converted into verified support or non-support. -See [`samples/Icod.Terminal.TermInfoPersistentRaster.Sample`](samples/Icod.Terminal.TermInfoPersistentRaster.Sample/README.md) for executable documentation and [`docs/releases/1.11.1.md`](docs/releases/1.11.1.md) for the patch-release contract. +See [`samples/Icod.Terminal.TermInfoPersistentRaster.Sample`](samples/Icod.Terminal.TermInfoPersistentRaster.Sample/README.md) for executable documentation and [`docs/releases/1.11.1.md`](docs/releases/1.11.1.md) for the integration-patch contract. ## Core 1.x guarantees @@ -231,7 +245,7 @@ The stable 1.x surface includes: - bounded device/status/cursor/style/color/clipboard/notification queries; - titles, current location, hyperlinks, clipboard operations, cursor style, synchronized output, progress, pointer shape, notifications, prompt/shell metadata, and terminal colors; - backend-neutral ephemeral raster display through verified Sixel and Kitty Graphics; -- backend-neutral persistent raster resources and placements with bounded generation-scoped ownership; +- backend-neutral persistent raster resources and placements with bounded generation-scoped ownership, source-pixel cropping, and signed z-order; - optional consumer-owned TermInfo lifecycle planning integration without adding Inspection to the production package graph. The library deliberately does not expose generic raw vendor dispatch as the ordinary extension model. @@ -245,7 +259,7 @@ The [`samples`](samples/README.md) directory contains focused examples. Recommen - `Icod.Terminal.CapabilityPlanning.Sample` — inspect-first semantic planning plus optional explicit verification; - `Icod.Terminal.Query.Sample` — bounded terminal queries; - `Icod.Terminal.RasterGraphics.Sample` — backend-neutral ephemeral raster display; -- `Icod.Terminal.PersistentRaster.Sample` — verify, create, place, update, and dispose persistent raster ownership without protocol ids/backend branching; +- `Icod.Terminal.PersistentRaster.Sample` — verify, create, crop/place, update z-order, and dispose persistent raster ownership without protocol ids/backend branching; - `Icod.Terminal.TermInfoPersistentRaster.Sample` — static TermInfo lifecycle plan, optional live Terminal verification, caller-owned replan, and persistent execution; - focused state, color, notification, prompt, and shell-integration samples described in the sample catalog. @@ -259,7 +273,7 @@ Terminal protocol traffic is external input/output. `Icod.Terminal` validates an Persistent raster acknowledgement correlation establishes transaction ownership, not trust. A terminal may independently evict stored image data; correlated `ENOENT` invalidates local terminal-resident certainty rather than triggering hidden replay. -Kitty direct transfer remains the reviewed persistent transport. Version 1.11 does not silently use file, temporary-file, or shared-memory transport and does not retain arbitrary source images after successful creation. +Kitty direct transfer remains the reviewed persistent transport. Version 1.12 does not silently use file, temporary-file, or shared-memory transport and does not retain arbitrary source images after successful creation. Source rectangles select already-owned source pixels; they do not create a new filesystem or external-memory transport. Several APIs intentionally publish caller-supplied metadata such as filesystem locations, hyperlinks, clipboard contents, notifications, shell metadata, command lines, and raster pixels. Applications decide what is appropriate to disclose. @@ -267,12 +281,12 @@ See [`docs/Security-and-Privacy.md`](docs/Security-and-Privacy.md). ## Compatibility -Stable `1.0.0` remains the compatibility floor. Versions 1.1–1.4 added compatible semantic protocol surfaces; 1.5 and 1.6 normalized internal control/query infrastructure; 1.7 introduced backend-neutral raster display; 1.8 added Kitty Graphics beneath that surface; 1.9 added protocol-neutral semantic events; 1.10 added semantic capability planning; 1.11 adds opaque persistent raster resource/placement ownership; and 1.11.1 adds no production API, instead qualifying the optional TermInfo persistent-raster lifecycle integration boundary. +Stable `1.0.0` remains the compatibility floor. Versions 1.1–1.4 added compatible semantic protocol surfaces; 1.5 and 1.6 normalized internal control/query infrastructure; 1.7 introduced backend-neutral raster display; 1.8 added Kitty Graphics beneath that surface; 1.9 added protocol-neutral semantic events; 1.10 added semantic capability planning; 1.11 added opaque persistent raster resource/placement ownership; 1.11.1 qualified the optional TermInfo persistent-raster lifecycle integration boundary; and 1.12 adds bounded source-pixel cropping and signed z-order without widening ownership into scene layout. -The final 1.11 public API fingerprint, retained by 1.11.1, is: +The final 1.12 public API fingerprint is: ```text -9336a1f6def1c4b02e86db813bae27f45b95af33f47a2cf10dccd4d1d44324f2 +eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8 ``` See [`docs/Compatibility-and-Versioning.md`](docs/Compatibility-and-Versioning.md). Consumers upgrading from the pre-1.0 line should also review [`docs/Migration-to-1.0.md`](docs/Migration-to-1.0.md). @@ -281,10 +295,10 @@ See [`docs/Compatibility-and-Versioning.md`](docs/Compatibility-and-Versioning.m Start with: -- [1.11.1 release notes](docs/releases/1.11.1.md) +- [1.12.0 release notes](docs/releases/1.12.0.md) - [Persistent Raster Ownership](docs/Persistent-Raster-Ownership.md) - [Capability Inspection and Planning](docs/Capability-Inspection-and-Planning.md) -- [1.11.1 development roadmap](Icod.Terminal-1.11.1-Development-Roadmap.md) +- [1.12.0 development roadmap](Icod.Terminal-1.12.0-Development-Roadmap.md) - [Current development roadmap](Icod.Terminal-Development-Roadmap.md) - [Architecture](docs/Architecture.md) - [Input and Events](docs/Input-and-Events.md) diff --git a/docs/Architecture.md b/docs/Architecture.md index 0fbd3fbab..a0323db2b 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -24,75 +24,49 @@ future Icod.Pty is adjacent: it may create child-process PTYs/ConPTYs, but it is not part of the Icod.Terminal runtime dependency chain. ``` -### `Icod.TermInfo` - -`Icod.TermInfo` owns immutable terminal capability data, terminfo interpretation/expansion, and description resolution. `Icod.Terminal` consumes that information; it does not maintain a competing capability database. - -### `Icod.Terminal` +`Icod.TermInfo` owns immutable terminal capability data and expansion. `Icod.Terminal` consumes that information; it does not maintain a competing description database. `Icod.Terminal` owns one live terminal conversation and the mechanics required to use it safely: - endpoint observation and native platform identity; - terminal-mode capture and semantic input policy; -- exact restoration of captured host state where promised; -- live dimensions and lifecycle observation; +- lifecycle and exact-restoration ownership where promised; - one authoritative input-reader/decoder path; - active query/response correlation; - unsolicited protocol-neutral semantic event routing; - semantic capability evidence, side-effect-free inspection, and bounded explicit verification; - bounded incremental control-language parsing; - semantic terminal-output operations; -- reversible presentation, rich-input, and color ownership; +- reversible presentation/input/color ownership; - backend-neutral ephemeral raster output; - persistent terminal-resident raster resource and placement ownership; +- bounded source-pixel placement cropping and signed z-order; - evidence-driven backend selection behind semantic operations; -- serialization of session-managed terminal output; -- lifecycle-aware invalidation, re-entry, and deterministic cleanup. - -### `Icod.DCurses` +- lifecycle-aware invalidation and deterministic cleanup. `Icod.DCurses` owns two-dimensional presentation policy: cells, styles, windows, pads, virtual-screen state, Unicode display width, clipping, wrapping, scrolling, damage tracking, desired-vs-physical screen comparison, refresh strategy, and higher-level scene/layout policy. -It may consume semantic capability planning, ephemeral raster display, or persistent raster resources from `Icod.Terminal`; it should not reimplement terminal modes, query routing, semantic-event routing, Sixel/Kitty framing, capability evidence, persistent protocol identity, or lifecycle restoration. - -### Future `Icod.Pty` - -Pseudo-terminal creation, child-process hosting, ConPTY/PTY plumbing, and process ownership remain outside the `Icod.Terminal` 1.x contract. +Pseudo-terminal creation, child-process hosting, ConPTY/PTY plumbing, and process ownership remain outside the `Icod.Terminal` 1.x runtime dependency chain. -## 2. Three abstraction levels +## 2. Abstraction levels -### 2.1 Ordinary semantic session API +### 2.1 Semantic session API -This is the preferred level for applications. - -`TerminalSession` exposes semantic operations for: - -- reading normalized terminal events; -- inspecting current semantic capability knowledge without terminal I/O; -- explicitly requesting bounded verification where a reviewed live probe exists; -- querying live terminal state through typed query methods; -- writing application text and reviewed semantic terminal metadata/control operations; -- acquiring reversible presentation/input/color state; -- displaying backend-neutral raw raster images ephemerally; -- creating generation-scoped persistent raster resources and placements where explicitly verified. +Applications normally use `TerminalSession` for normalized input/events, typed queries, semantic output/state operations, capability planning, raster display, and persistent resource/placement ownership. These APIs participate in session ordering, validation, resource bounds, capability evidence, lifecycle, and cleanup semantics. ### 2.2 Advanced transport/provider API -`ITerminalInput`, `ITerminalOutput`, `ITerminalControlProvider`, `TerminalEndpoint`, native mode snapshots, and controlled result types remain public for custom hosts, injected transports, diagnostics, and higher-level libraries operating below the semantic layer. +`ITerminalInput`, `ITerminalOutput`, `ITerminalControlProvider`, `TerminalEndpoint`, native mode snapshots, and controlled result types remain public for injected transports, diagnostics, and higher-level libraries. -These contracts do not imply that a live session can be bypassed safely. In particular, a `TerminalSession` owns the authoritative input-reader path while active. - -`TerminalSession.Output` is a borrowed advanced escape hatch. Direct use is outside session serialization; callers accept responsibility for avoiding interleaving with session-managed traffic. +A live session still owns the authoritative reader. `TerminalSession.Output` is a borrowed advanced escape hatch outside ordinary session serialization. ### 2.3 Internal wire/platform machinery -Protocol encoders/parsers, control-family writers, Sixel quantization/encoding, Kitty Graphics adaptation/chunking/persistent ids, semantic-event recognition, query transactions, capability evidence storage, lifecycle signal sources, presentation/input managers, and OS plumbing remain implementation details unless represented separately by a public semantic contract. - -Internal selectors and numeric protocol identities are not compatibility promises merely because public semantic APIs ultimately use them. +Protocol encoders/parsers, control-family writers, Sixel quantization/encoding, Kitty Graphics adaptation/chunking/persistent ids, semantic-event recognition, query transactions, capability evidence storage, lifecycle sources, and OS plumbing remain implementation details unless represented separately by a public semantic contract. -## 3. Capability-driven, not terminal-brand-driven +## 3. Capability-driven routing The normalized model separates: @@ -105,31 +79,11 @@ evidence source endpoint availability ``` -Static TermInfo/profile advertisement and generation-scoped live evidence are distinct. A terminal name, `TERM`, environment variable, host OS, emulator brand, registry order, or caller preference is not automatically capability proof. - -For Sixel, Primary Device Attributes parameter `4` may provide positive support evidence. A valid response without `4`, silence, timeout, or caller cancellation does not automatically prove terminal-wide unsupported Sixel. - -For Kitty Graphics, the reviewed support path uses the protocol-defined correlated query plus Primary DA barrier semantics. A valid correlated Kitty response verifies that backend for the current generation; Primary DA arriving first is reviewed negative evidence for that concrete probe; silence before either authoritative result remains unknown. - -### Public capability planning - -The public vocabulary is deliberately reduced: - -```text -TerminalCapability -TerminalCapabilitySupport -TerminalCapabilityEndpointAvailability -TerminalCapabilityEvidenceKind -TerminalCapabilityStatus -``` - -`TerminalSession.InspectCapability(...)` is synchronous and side-effect free. It projects existing knowledge only. +Static terminal-description evidence and generation-scoped live evidence are distinct. Terminal names, `TERM`, environment variables, host OS, emulator brands, registry order, and caller preferences are not automatically capability proof. -`TerminalSession.VerifyCapabilityAsync(...)` is explicit because it may emit bounded probe traffic. It reuses reviewed existing probe paths rather than inventing traffic for every semantic capability. +`TerminalSession.InspectCapability(...)` is side-effect free. `VerifyCapabilityAsync(...)` is explicit because it may emit a bounded reviewed live probe. -Public evidence is only `None`, `StaticDescription`, or `LiveObservation`; backend ids, routing scores, raw protocol frames, and `Icod.TermInfo` provenance remain private. - -Version 1.11 adds `PersistentRasterGraphics = 9`. It is separate from ordinary `RasterGraphics`: Sixel may satisfy ephemeral raster display, while persistent terminal-resident resource ownership requires the reviewed persistent-capable Kitty Graphics path. +`PersistentRasterGraphics = 9` is separate from ordinary `RasterGraphics`: verified Sixel may satisfy ephemeral raster display while persistent terminal-resident ownership requires the reviewed persistent-capable Kitty Graphics path. ## 4. Control-language layering @@ -145,42 +99,29 @@ semantic intent The normalized framing vocabulary includes CSI, DCS, OSC, APC, PM, and SOS. -Raster examples make the separation concrete: +Raster routing illustrates the boundary: ```text RasterGraphics - -> DcsSixel - -> DCS - -> Sixel + -> DcsSixel -> DCS -> Sixel RasterGraphics - -> ApcKittyGraphics - -> APC - -> Kitty Graphics + -> ApcKittyGraphics -> APC -> Kitty Graphics PersistentRasterGraphics - -> ApcKittyGraphics - -> APC - -> private resource / placement protocol identity + -> ApcKittyGraphics -> APC + -> private resource / placement protocol identity ``` The public raster/resource contracts remain semantic even though the current persistent implementation is Kitty-specific internally. ## 5. Backend-neutral raster data -`TerminalRasterImage`, `TerminalRasterPixelFormat`, and `TerminalRasterColor` represent bounded raw image data, not a Sixel/Kitty payload container. - -Supported storage forms are: - -```text -Rgb24 -Rgba32 -Indexed8 + RGBA8 palette -``` +`TerminalRasterImage`, `TerminalRasterPixelFormat`, and `TerminalRasterColor` represent bounded raw image data rather than protocol payload containers. -The raster object owns a snapshot of caller-provided pixel/palette storage. Straight alpha is preserved. Mutable backing buffers are not exposed publicly. +Supported storage forms are `Rgb24`, `Rgba32`, and `Indexed8` plus RGBA8 palette. -Raster ceilings are explicit: +Raster ceilings remain: ```text maximum dimension 16,384 @@ -193,46 +134,15 @@ Image-file decoding, gamma/color-profile processing, and hidden background compo ## 6. Ephemeral raster display -`TerminalSession.DisplayRasterAsync(...)` represents ephemeral display intent. +`TerminalSession.DisplayRasterAsync(...)` represents ephemeral display intent. Reviewed backends are verified Kitty Graphics and verified Sixel. -Its reviewed internal backends are: +Routing happens before commitment. Once a backend commits output, transport/protocol failure is surfaced and the library does not replay through another backend because the terminal may have applied an unknown prefix. -```text -verified ApcKittyGraphics -verified DcsSixel -``` +Large Sixel and Kitty transfers are emitted as bounded lazy segments/chunks rather than requiring one complete encoded transfer allocation. -When both are verified, Kitty Graphics is preferred; verified Sixel remains fallback. Routing decisions happen before commitment. +## 7. Persistent raster ownership — 1.11+ -Once a backend commits output, transport/protocol failure is surfaced. The library does not replay through another backend because the terminal may have applied an unknown prefix. - -The Sixel pipeline is: - -```text -TerminalRasterImage - -> deterministic bounded quantization - -> SixelPaletteImage - -> bounded lazy payload segments - -> serialized DCS transaction -``` - -The Kitty direct-transfer pipeline is: - -```text -TerminalRasterImage - -> checked raw adaptation - -> RGB24 or RGBA32 byte stream - -> bounded lazy Base64 chunks - -> serialized APC frame sequence -``` - -Large images do not require one giant encoded string/allocation. - -## 7. Persistent raster ownership — 1.11 - -Version 1.11 adds a separate terminal-resident ownership domain rather than widening `DisplayRasterAsync(...)` into a scene graph. - -The semantic ownership graph is: +Version 1.11 established a separate terminal-resident ownership domain: ```text TerminalSession @@ -242,60 +152,54 @@ TerminalSession -> ... ``` -`TerminalSession.CreateRasterResourceAsync(...)` publishes a public resource only after acknowledged upload establishes a private terminal identity. - -`TerminalRasterResource.CreatePlacementAsync(...)` creates opaque child ownership. `TerminalRasterPlacement.UpdateAsync(...)` replaces the same private placement at the terminal's current cursor position. - -`TerminalRasterPlacementOptions.Columns` and `.Rows` are independently optional and bounded to `1..16384`. The reviewed backend uses no-cursor-movement placement semantics. - -### Private identity +A public resource is published only after acknowledged upload establishes a private terminal identity. Placement create/update likewise uses correlated acknowledgement through the authoritative query/input path. -Internally, resource upload uses a nonzero private image number for acknowledgement correlation and receives a nonzero terminal-assigned image id. Placement identity is likewise private and nonzero. +Placement position remains the terminal's current cursor location. `Columns` and `Rows` are independently optional and bounded to `1..16384`. -Public resources/placements never expose those ids. Callers cannot manufacture raw resource/placement protocol identity. +### 7.1 Source-pixel cropping — 1.12 -### Bounded registries +Version 1.12 adds `TerminalRasterSourceRectangle` and `TerminalRasterPlacementOptions.SourceRectangle`. -Session bookkeeping is bounded: +Coordinates are zero-based source-image pixels. `Width` and `Height` are positive. Scalar values remain within the raster dimension ceiling, and the complete rectangle must fit inside the owning resource before placement output commits. -```text -256 live persistent resources -4096 live persistent placements -``` +The resource state stores immutable source width/height metadata needed for this validation. It does not retain source pixel bytes for replay. -Allocation avoids live collisions and handles numeric wraparound. These are local ownership bounds, not terminal storage-quota promises. +Source cropping does **not** change screen placement ownership: the placement still occurs at the current cursor. -### No hidden raster cache +### 7.2 Signed z-order — 1.12 -After successful creation, the persistent registry retains ownership metadata, not an arbitrary hidden `TerminalRasterImage` copy. Version 1.11 therefore does not promise automatic re-upload/rebind. +`TerminalRasterPlacementOptions.ZIndex` is nullable signed `int` and accepts the full CLR `int` domain. -See `Persistent-Raster-Ownership.md` for the full public ownership contract. +It expresses relative stacking intent to the reviewed persistent backend. It is not a scene graph, parent/child placement chain, or global composition policy. -## 8. Alpha and image semantics +### 7.3 Shared placement transaction -Kitty direct RGBA32 can preserve fractional alpha. Sixel cannot represent equivalent semantics without external compositing policy, so fractional-alpha display through Sixel is controlled unsupported rather than silently flattened. +Create and update use the same acknowledged placement transaction. The reviewed deterministic backend order is: -Indexed input expands for Kitty only as required by its raw direct formats: opaque referenced palette colors permit RGB24; any referenced non-opaque color requires RGBA32. - -Persistent resource creation reuses the same bounded raw adaptation semantics and does not add image decoding/transcoding. +```text +Ga=p,i=,p=,C=1[,x=...[,y=...[,w=...[,h=...]]]][,c=...][,r=...][,z=...] +``` -## 9. Session-managed output ordering +When a source rectangle is present, all four crop fields are emitted together. Signed z-order uses invariant decimal formatting. When 1.12 options are absent, existing 1.11 bytes/behavior are preserved. -High-level application text, semantic output, query requests, reversible state traffic, ephemeral raster output, and persistent raster transactions use session-owned serialization domains appropriate to their contracts. +### 7.4 Private identity and bounded registries -Committed Sixel output holds the session output gate through final ST/flush. Committed Kitty direct output holds it across all APC frames through final flush. +Image numbers, terminal image ids, and placement ids remain private and nonzero. Public callers cannot manufacture them. -Persistent resource upload is also one logical committed transfer. Placement create/update are serialized operations coordinated with acknowledgement through the existing query manager. +Session bookkeeping remains bounded: -Caller cancellation is honored before commitment. After commitment, ordinary cancellation does not intentionally truncate the logical graphics transaction. +```text +256 live persistent resources +4096 live persistent placements +``` -Session teardown drains committed output before final output-state restoration continues. +These are local ownership bounds, not terminal storage-quota promises. -## 10. One authoritative input conversation +## 8. One authoritative input conversation -A live session owns one incremental byte stream containing ordinary text/keys/paste/mouse/focus data, lifecycle traffic, active query responses, unsolicited semantic reports, graphics probe responses, and persistent graphics acknowledgements. +A live session owns one incremental byte stream containing ordinary input, lifecycle traffic, active query responses, unsolicited semantic reports, graphics probe replies, and persistent graphics acknowledgements. -The stable precedence is: +Stable precedence remains: ```text active query/response ownership @@ -303,108 +207,85 @@ active query/response ownership -> ordinary application-input decoding ``` -No raster or persistent-resource feature creates a graphics-specific reader. - -Resource upload and placement create/update acknowledgements are correlated through the same transaction/query authority. Wrong private identities do not satisfy another transaction. +No raster or persistent-resource feature creates a competing reader. -## 11. Correlation grants ownership, not trust +Wrong persistent identities do not satisfy another transaction. Timeout/late-response correlation remains bounded and does not allow a stale acknowledgement to complete a later placement. -Terminal responses remain untrusted after they become transaction-owned. +## 9. Correlation grants ownership, not trust -Matching identifiers do not bypass grammar, termination, size, duplicate-field, or numeric-overflow validation. Malformed/oversized owned responses are recovered boundedly rather than leaked into ordinary application input. +Matching identifiers establish bounded routing ownership, not terminal authenticity. Malformed framing, numeric overflow, duplicate fields, size bounds, and response grammar remain validated after correlation. -For persistent graphics, a well-formed correlated `ENOENT` means the terminal no longer recognizes an object the session believed current. That invalidates terminal-resident certainty; it is not permission for hidden replay. +A well-formed correlated `ENOENT` means the terminal no longer recognizes an object the session believed current. That invalidates terminal-resident certainty; it does not authorize hidden replay or imply anything about unrelated state. -## 12. Ownership and reversible state +## 10. Output commitment -A `TerminalSession` owns terminal state transitions, not necessarily the underlying descriptor/stream/transport. +Caller cancellation is honored before commitment where possible. Once a logical graphics transaction commits, ordinary cancellation does not intentionally truncate it. -Supplied transports remain borrowed. Disposal restores state the session changed but does not close caller-owned transports. +Persistent resource upload is one committed acknowledged transaction. Placement create/update uses the same serialized session/query authority. Post-commit transport failure is surfaced without blind replay, automatic backend switching, or invented terminal certainty. -Reversible terminal features use leases when consumers may overlap. Exact restoration is based on observed/captured state rather than guessed defaults. +## 11. Generation-scoped ownership -Ephemeral raster display is output, not reversible state. +Persistent raster resources and placements are not exactly restorable state. They are explicit generation-scoped terminal-resident ownership. -Persistent raster resources are also **not exactly restorable state**. They are explicit generation-scoped terminal-resident ownership. While identity is current, the session can target cleanup. Once lifecycle uncertainty invalidates identity, stale handles perform local-only cleanup and no stale numeric identifiers are emitted. +`InvalidateState()` and lifecycle generation changes make existing identities stale. Thereafter: -The library does not retain/replay resources merely to simulate restoration. +- placement update returns controlled `Unavailable` before output; +- new placement creation from a stale resource returns controlled `Unavailable` before output; +- stale disposal releases local ownership only; +- no stale numeric protocol identity is emitted; +- no hidden source raster is replayed or re-uploaded. -## 13. Lifecycle as a trust boundary +Applications explicitly create new resources when current persistent ownership is needed again. -Suspend/resume and explicit invalidation are state transitions. +## 12. Deterministic cleanup -Before suspension, reversible owned state is restored as required. After resume, generation-scoped live observations and persistent terminal-resident identity certainty expire; configured reversible state is re-established according to its own contract. +Placement disposal is locally idempotent and, while current, attempts one quiet targeted delete. -Already-returned capability statuses are immutable snapshots. Callers inspect/verify again when current knowledge matters. +Resource disposal prevents new children, closes child ownership first, attempts child deletion before resource-data deletion, releases local ownership even if cleanup transport fails, and aggregates multiple cleanup failures where necessary. -Persistent handles do not revive automatically after generation invalidation. Applications create new resources explicitly if they still need them. +Session teardown drains committed transactions and deletes current placements before current resource data. Already-stale state receives local-only cleanup. -## 14. Deterministic persistent cleanup +## 13. Security-sensitive transport choices -Placement disposal releases local ownership once and, while current, attempts one targeted quiet delete. It is locally idempotent even when terminal cleanup transport fails. - -Resource disposal prevents new children, closes child ownership first, attempts child cleanup before resource-data deletion, and aggregates multiple cleanup failures if necessary. - -Session teardown deletes current placements before current resource data. If persistent state is already stale, teardown performs local bookkeeping only. - -## 15. Failure and uncertainty - -The architecture favors truthful uncertainty over optimistic advancement. - -- query silence does not automatically become unsupported truth; -- unavailable endpoints are distinct from unsupported capability; -- correlated malformed responses are failures, not support evidence; -- partial committed graphics output is not replayed automatically; -- terminal `ENOENT` invalidates current resource certainty; -- cleanup failures are surfaced rather than hidden behind invented success; -- stale resource identity is not emitted after lifecycle invalidation. - -## 16. Bounded work and storage - -Documented bounds are part of the safety architecture: - -```text -normal terminal response frame 4,096 bytes -small complete DCS frame 4,096 bytes -small complete APC frame 8,192 bytes -Kitty Base64 image data per APC chunk 4,096 bytes -persistent resources 256/session -persistent placements 4096/session -``` - -Parser, query, semantic-event, raster conversion, and ownership registries remain bounded. - -## 17. Security-sensitive transport choices - -Kitty direct transfer remains the reviewed raster transport. The library does not silently choose file, temporary-file, or shared-memory transfer because those introduce path naming, permissions, lifetime, visibility, race, and cross-process concerns. +Kitty direct transfer remains the reviewed persistent transport. File, temporary-file, and shared-memory transfer are not selected silently because they introduce path, permissions, lifetime, visibility, race, and cross-process concerns. Base64 is protocol framing, not encryption. -Persistent storage is owned by the terminal and may be evicted according to terminal policy; local registry bounds do not imply terminal storage reservation. +Persistent source cropping operates on already-owned source pixels and does not introduce an external storage transport. -## 18. Stable architectural exclusions +## 14. Stable exclusions after 1.12 -Stable 1.x does not treat the following as ordinary `Icod.Terminal` responsibilities: +Stable 1.x still does not treat the following as ordinary `Icod.Terminal` responsibilities: - process-global current-terminal state; - competing live input readers; - generic raw vendor control/event buses; - terminal-brand-driven capability proof; - image-file decoding/transcoding; -- hidden graphics replay; +- hidden graphics replay or retained persistent source-image cache; - unbounded graphics/query/event state; +- public raster backend selection or public Kitty ids; +- relative placement graphs or parent placement identities; +- absolute screen-coordinate placement / Terminal-owned layout; +- Unicode placeholder/virtual placements; +- animation/frame lifecycle; - PTY/ConPTY process hosting; - cells, windows, layout, damage, or scene-graph ownership. -Advanced persistent placement features such as source rectangles, z-order, Unicode placeholders, relative/pixel placement, and animation require separate review and downstream justification. +Source rectangles and z-order are the complete 1.12 advanced placement feature set; they do not imply the excluded scene-layout features. + +## 15. Dependency boundary -## 19. Dependency boundary +`Icod.Terminal.csproj` is the direct NuGet dependency authority. The 1.12 production graph remains: -`Icod.Terminal.csproj` is the direct NuGet dependency authority. Tests, samples, package consumers, and verification tools do not independently pin exact transitive runtime dependency versions merely to duplicate package metadata. +```text +Icod.TermInfo 1.11.0 +Icod.Timing 1.0.0 +``` -Successful restore/build of the declared package graph remains the normal dependency-compatibility witness. +Tests, samples, and package consumers may use additional dependencies for qualification without widening the production graph. -## 20. Permanent authorities +## 16. Permanent authorities Related 1.x authorities include: diff --git a/docs/Compatibility-and-Versioning.md b/docs/Compatibility-and-Versioning.md index 23451fba1..2ff3bee20 100644 --- a/docs/Compatibility-and-Versioning.md +++ b/docs/Compatibility-and-Versioning.md @@ -1,443 +1,202 @@ # Compatibility and Versioning -This document defines the permanent compatibility and versioning policy for the `Icod.Terminal` 1.x line. +This document defines the permanent stable 1.x compatibility policy for `Icod.Terminal`. -The public API fingerprint, permanent semantic documentation, package contracts, and downstream acceptance tests together define the supported 1.x contract. Compatibility is not limited to source compilation: documented ownership, restoration, cancellation, query routing, resource bounds, capability evidence, semantic event routing, committed-output behavior, and security guarantees are compatibility commitments too. +## 1. Stable compatibility floor -## 1. Versioning model +`1.0.0` is the stable compatibility floor. -`Icod.Terminal` uses semantic versioning. +Within the 1.x line, releases are expected to preserve existing public signatures, enum numeric values, result/status semantics, ownership/lifecycle guarantees, and documented protocol-neutral behavior except where a later compatible release adds optional functionality. -For stable 1.x releases: +A minor release may add new public members and new semantic capabilities. It must not silently repurpose existing public members or require callers to opt into new behavior merely to retain previously documented semantics. -- a **patch** release fixes defects, strengthens tests/documentation, improves performance, or hardens implementation without intentionally breaking the documented 1.x contract; -- a **minor** release may add compatible public APIs, semantic protocol support, or optional behavior while preserving existing public signatures and documented guarantees; -- a **major** release is required for ordinary intentional source/binary breaks, removal or incompatible reinterpretation of public members, enum renumbering, or incompatible changes to documented ownership/security/restoration semantics. +## 2. Supported target frameworks -A bug fix may change behavior when the previous behavior violated an already-documented contract, but compatibility-sensitive corrections must still be documented. - -## 2. Public API baselines - -The stable `1.0.0` exported surface remains frozen by: - -- `docs/Public-API-Baseline-1.0.md`; -- `docs/Public-API-Baseline-1.0.sha256`. - -Compatible minor-release additions receive separate reviewed baselines rather than overwriting earlier evidence: - -- `1.1` — additive OSC 633 surface; -- `1.2` — additive OSC 777 titled-notification surface; -- `1.3` — additive typed iTerm2 OSC 1337 surface; -- `1.4` — additive typed Kitty OSC 99 notification/query surface; -- `1.7` — additive backend-neutral raster-display surface; -- `1.9` — additive protocol-neutral semantic-event envelope and interactive Kitty notification options; -- `1.10` — additive protocol-neutral semantic capability inspection/planning surface; -- `1.11` — additive persistent-raster capability and opaque resource/placement ownership surface. - -Versions `1.5.0` and `1.6.0` intentionally added no public API and retained the 1.4 fingerprint: - -```text -3654594768a0e47be7c43820bef96779739e12ce4b710d4eaca43308bef86b27 -``` - -Version `1.7.0` advanced the fingerprint to: - -```text -847441fb4a8cdc89979aca9e96178f939895b93ec19a973232210af09716f700 -``` - -Versions `1.8.0` and `1.8.1` intentionally added no public API and retained that fingerprint. - -Version `1.9.0` advanced the fingerprint to: - -```text -e652e6fd65cd43422ca84b7c4c2a1815ee7ead9b2a64285e0e17cf39614b0315 -``` - -Version `1.10.0` advanced the fingerprint to: - -```text -ee705250d19d51df92645e5020f188646dd2dbf38483278e6e57ce6fbbc1e9fb -``` - -Version `1.11.0` intentionally advances the authoritative current public API fingerprint to: +The stable package targets: ```text -9336a1f6def1c4b02e86db813bae27f45b95af33f47a2cf10dccd4d1d44324f2 +net8.0 +net9.0 +net10.0 ``` -The authoritative current baseline is: - -- `docs/Public-API-Baseline-1.11.md`; -- `docs/Public-API-Baseline-1.11.sha256`. - -Historical baselines remain checked in unchanged as compatibility evidence. - -`packaging/VerifyPublicApiBaseline.ps1` regenerates the reflection snapshot independently for `net8.0`, `net9.0`, and `net10.0`, proves that all three exported surfaces agree, and verifies the authoritative current fingerprint. The fingerprint is a review gate, not a promise that 1.x can never grow; intentional compatible additions require an explicit new baseline in the same reviewed minor release. - -## 3. Source and binary compatibility +A stable release must qualify the public package/runtime graph on all supported TFMs and on Windows, Linux, and macOS through the repository's release-validation matrix. -Within stable 1.x, ordinary releases preserve existing public type/member names and signatures. +## 3. Public API baselines -Compatibility-sensitive changes include: +Every API-bearing stable minor release records a deterministic reflection snapshot and SHA256 fingerprint. Historical baselines are immutable evidence and are never rewritten merely because a later release adds compatible members. -- removing or renaming a public type/member; -- changing parameter order or types; -- changing return types incompatibly; -- making optional parameters required; -- tightening nullability in a way that rejects previously valid calls; -- changing implemented public interfaces incompatibly; -- changing public enum numeric values; -- changing public constant values callers may have compiled into assemblies; -- changing an established semantic operation from supported behavior to unconditional failure without an exceptional compatibility reason. - -Compatible overloads, new types, and new semantic operations may be introduced in a minor release when they do not make existing behavior ambiguous or unsafe. - -## 4. Stable enum numerics - -Existing public enum numeric values are stable throughout 1.x. Existing values must not be renumbered or reused for another meaning. - -Adding an enum value is compatibility-sensitive even when binary-compatible. It requires a minor release, explicit review, a baseline update, and documentation for callers with exhaustive switches. - -Known frozen values include: +Relevant fingerprints include: ```text -TerminalEventKind - Input = 0 - Lifecycle = 1 - Timeout = 2 - Cancelled = 3 - Semantic = 4 - -TerminalRasterPixelFormat - Rgb24 = 0 - Rgba32 = 1 - Indexed8 = 2 - -TerminalCapability - ClipboardRead = 0 - ClipboardWrite = 1 - CursorStyle = 2 - SynchronizedOutput = 3 - KeyboardReporting = 4 - MouseReporting = 5 - FocusReporting = 6 - BracketedPaste = 7 - RasterGraphics = 8 - PersistentRasterGraphics = 9 - -TerminalCapabilitySupport - Unknown = 0 - Unsupported = 1 - Advertised = 2 - Verified = 3 - -TerminalCapabilityEndpointAvailability - Unavailable = 0 - Available = 1 - -TerminalCapabilityEvidenceKind - None = 0 - StaticDescription = 1 - LiveObservation = 2 +1.9 e652e6fd65cd43422ca84b7c4c2a1815ee7ead9b2a64285e0e17cf39614b0315 +1.10 ee705250d19d51df92645e5020f188646dd2dbf38483278e6e57ce6fbbc1e9fb +1.11 9336a1f6def1c4b02e86db813bae27f45b95af33f47a2cf10dccd4d1d44324f2 +1.12 eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8 ``` -The 1.10 capability-planning numerics remain stable, and 1.11 appends `PersistentRasterGraphics = 9` without renumbering values `0..8`. - -## 5. Behavioral compatibility +Version 1.11.1 intentionally retained the 1.11 fingerprint because it added no production public API. -Permanent documents under `docs/` define behavioral guarantees versioned alongside the API. +## 4. 1.12 additive public API -Stable guarantees include: - -- one authoritative live-session input reader; -- active query response ownership preceding unsolicited semantic-event recognition, which precedes ordinary input decoding; -- no double delivery of one frame as both query response and semantic event; -- bounded semantic-event buffering in the same application-event ordering domain as ordinary input; -- bounded malformed/oversized owned-frame recovery; -- query correlation and bounded late-response ownership; -- pre-commit versus post-commit cancellation semantics; -- truthful `Unavailable`, `Unsupported`, `Unknown`, and failure distinctions; -- exact restoration only where explicitly promised; -- session/lease ownership and disposal authority; -- bounded parser/query/raster work; -- output serialization boundaries; -- static description evidence distinct from generation-scoped live observation; -- lifecycle invalidation expiring generation-scoped live evidence; -- terminal/vendor identity and caller preference not being capability proof; -- query timeout not automatically becoming unsupported truth; -- committed graphics output not intentionally truncated by ordinary caller cancellation; -- partial committed graphics failure surfaced without automatic replay/backend switching; -- persistent raster identities scoped to the lifecycle generation that established them; -- no automatic persistent-raster replay/re-upload after lifecycle uncertainty; -- child placement cleanup preceding resource-data cleanup while identities are current; -- stale persistent handles never emitting stale terminal identifiers during disposal; -- teardown draining committed output before output-state restoration; -- correlated terminal responses remaining untrusted and bounded after ownership is established. - -Minor/patch releases may strengthen correctness while preserving these guarantees, but must not silently weaken or reverse them. - -## 6. Raster compatibility contract - -The public raster model is backend-neutral. It represents bounded raw image data plus semantic graphics intent, not “a Sixel image” or “a Kitty image.” - -Version 1.7 added: +The only intended additions relative to 1.11 are: ```text -TerminalRasterPixelFormat -TerminalRasterColor -TerminalRasterImage -TerminalSession.DisplayRasterAsync(...) +TerminalRasterSourceRectangle +TerminalRasterSourceRectangle..ctor(int,int,int,int) +TerminalRasterSourceRectangle.X +TerminalRasterSourceRectangle.Y +TerminalRasterSourceRectangle.Width +TerminalRasterSourceRectangle.Height +TerminalRasterPlacementOptions.SourceRectangle +TerminalRasterPlacementOptions.ZIndex ``` -The raster object owns a snapshot of caller-provided pixel/palette storage. RGB24 is opaque; RGBA32 and indexed palette colors preserve straight alpha. +All existing public signatures and enum numeric values are preserved. -Version 1.8 added Kitty Graphics below the unchanged ephemeral raster API while retaining Sixel fallback. The public contract does not expose a raw DCS/APC writer, backend selector, Kitty numeric image id, Kitty image number, Kitty placement id, arbitrary control-data dictionary, or scene graph. +No new `TerminalCapability` numeric value is introduced by 1.12. -Version 1.11 adds a separate persistent ownership domain: +## 5. Persistent-raster compatibility -```text -TerminalCapability.PersistentRasterGraphics -TerminalRasterResource -TerminalRasterPlacement -TerminalRasterPlacementOptions -TerminalSession.CreateRasterResourceAsync(...) -TerminalRasterResource.CreatePlacementAsync(...) -TerminalRasterPlacement.UpdateAsync(...) -``` +Version 1.11 established opaque persistent raster resources/placements and is the behavioral base for 1.12. -Persistent resources and placements are opaque session-owned handles. Their backend protocol identities remain private. Placement creation/update uses current-cursor positioning and optional `Columns`/`Rows`; callers continue to use ordinary terminal operations for cursor movement rather than receiving a scene-coordinate API. +The following remain compatible guarantees: -The existing ephemeral `DisplayRasterAsync(...)` contract remains compatible and may still resolve through verified Kitty Graphics or Sixel. Persistent ownership is a distinct capability and is not emulated through Sixel. +- public resource/placement protocol identities stay opaque; +- placement position stays the terminal's current cursor location; +- `Columns`/`Rows` remain independently optional and bounded to `1..16384`; +- create/update remain acknowledged operations through the authoritative query path; +- persistent ownership remains generation scoped; +- stale mutation returns controlled `Unavailable` before output; +- stale disposal remains local-only; +- live resource/placement ceilings remain `256` / `4096` per session; +- no hidden source-image cache or automatic replay is introduced; +- persistent transport remains direct Kitty transfer internally; +- current cleanup remains child placement before resource data. -Fractional alpha remains valid common raster data. Kitty RGBA32 preserves it; Sixel returns controlled unsupported when equivalent semantics cannot be represented truthfully rather than silently compositing. +### 5.1 1.12 optional placement geometry -## 7. Sixel and Kitty Graphics protocol compatibility +`SourceRectangle` and `ZIndex` are optional. When both are absent, the existing 1.11 persistent placement byte/semantic contract is preserved. -Sixel and Kitty Graphics remain internal backends beneath public semantic graphics contracts. +A caller upgrading from 1.11 does not need to change existing placement code. -Stable Sixel behavior includes canonical seven-bit DCS framing, deterministic bounded quantization, bounded lazy payload generation, caller cancellation before commitment but not intentional frame truncation after commitment, serialization through final ST/flush, and no automatic retry after partial transport failure. +When `SourceRectangle` is supplied: -Stable Kitty Graphics behavior includes canonical seven-bit APC framing, direct transfer (`t=d`), RGB24/RGBA32 raw transmission, deterministic Indexed8 expansion, Base64 image data bounded to 4096 bytes per protocol chunk, one logical multi-frame serialized transaction, no ordinary post-commit cancellation truncation, no automatic replay/Sixel switch after partial committed failure, and bounded correlated support-query ownership. +- it selects a source-image pixel region; +- it must fit completely inside the owning resource; +- invalid rectangles are rejected before new placement output; +- source dimensions are retained as metadata only, not as a replay pixel cache. -Version 1.11 additionally reviews the narrow persistent Kitty subset required to upload acknowledged terminal-resident image data, create/update placements, and delete placements/resources. Those commands remain internal implementation detail behind opaque public ownership objects. +When `ZIndex` is supplied, its complete signed `int` value is preserved as relative stacking intent. This does not create a general scene-layout contract. -File/temp-file/shared-memory Kitty transports, source rectangles, z-order, Unicode placeholders, relative placements, pixel-coordinate placement, animation, and scene-graph policy remain outside the 1.11 compatibility promise unless separately reviewed in a later release. +## 6. Query/input compatibility -## 8. Capability evidence and uncertainty +Stable 1.x preserves one authoritative terminal input conversation. -Protocol support and NuGet package compatibility are separate concerns. +Application input, lifecycle observations, query responses, semantic events, raster capability probes, and persistent-raster acknowledgements remain coordinated by the same reader/router model. -Successful semantic output proves emission, not terminal recognition or visual application unless a protocol supplies explicit acknowledged evidence. +Compatibility includes: -Static terminal description/profile advertisement and generation-scoped live observation are distinct. Terminal name, `TERM`, host OS, emulator brand, registry order, and caller preference are not capability proof. +- wrong query identities not satisfying another operation; +- malformed owned responses remaining owned/recovered according to bounded parsing rules rather than leaking into application input; +- timeout not automatically becoming unsupported capability truth; +- late responses not satisfying a later transaction with a different correlation identity; +- caller cancellation preserving the established pre-commit/post-commit distinction. -For Sixel, Primary DA attribute `4` may provide positive live support evidence; a valid DA response without `4`, timeout, or caller cancellation does not automatically prove terminal-wide unsupported behavior. +## 7. Capability-planning compatibility -For Kitty Graphics, a valid correlated probe response provides positive evidence; the reviewed Primary DA barrier arriving first provides negative evidence for that concrete probe; timeout before either authoritative result remains unknown; caller cancellation is not negative evidence. +Public capability planning remains semantic and dependency-neutral. -Live evidence expires under the existing session-generation invalidation contract. +`InspectCapability(...)` is side-effect free. `VerifyCapabilityAsync(...)` is explicit and uses only reviewed bounded live probes. -## 9. Version 1.10 capability-planning compatibility +Endpoint availability remains separate from support knowledge. Static advertisement, live verification, unknown state, and unsupported state are not collapsed merely to make routing simpler. -Version 1.10 is an additive minor release. It does not reinterpret existing raster/query/input contracts. +The internal table-driven TermInfo evidence cleanup completed for 1.12 is behavior preserving and does not alter public evidence states or package dependencies. -The public additions are: +## 8. Committed-output compatibility -```text -TerminalCapability -TerminalCapabilitySupport -TerminalCapabilityEndpointAvailability -TerminalCapabilityEvidenceKind -TerminalCapabilityStatus -TerminalSession.InspectCapability(...) -TerminalSession.VerifyCapabilityAsync(...) -``` +Committed graphics operations do not intentionally truncate after commitment merely because ordinary caller cancellation arrives. -`InspectCapability(...)` is synchronous and side-effect free. It reads current in-memory semantic knowledge only and emits no terminal traffic. +Partial transport failure is surfaced without blind replay or automatic backend switching. This applies to ephemeral raster output and persistent upload/placement transactions according to their existing logical transaction boundaries. -`VerifyCapabilityAsync(...)` is explicit and bounded. It may strengthen knowledge only through existing reviewed probe paths. Version 1.10 introduced live verification for `KeyboardReporting` and `RasterGraphics`; capabilities without a reviewed probe remain inspection-only rather than receiving invented traffic. +## 9. Package dependency compatibility -Support knowledge and endpoint availability are separate compatibility dimensions. A statically advertised capability may remain `Advertised` while the required endpoint is `Unavailable`; this makes `IsUsable` false without rewriting truthful support knowledge to `Unsupported`. +`Icod.Terminal.csproj` remains the direct production dependency authority. -Public evidence intentionally projects to only: +Version 1.12 keeps: ```text -None -StaticDescription -LiveObservation +Icod.TermInfo 1.11.0 +Icod.Timing 1.0.0 ``` -The public contract does not expose `Icod.TermInfo`, raw OSC/CSI/DCS/APC identities, Kitty/Sixel backend ids, routing scores, or terminal-brand heuristics. - -Generation-scoped live observations expire on lifecycle invalidation/resume while valid static description evidence remains. Already-returned `TerminalCapabilityStatus` values are immutable snapshots; callers inspect again for current knowledge. - -The permanent contract authority is `docs/Capability-Inspection-and-Planning.md`. - -### Version 1.11 persistent-raster capability +Tests, samples, and package-only consumers may reference extra tooling/inspection packages without making those dependencies part of the production package graph. -Version 1.11 appends `PersistentRasterGraphics = 9` to that semantic capability vocabulary. +Stable package qualification verifies restore/build and executable NuGet-only consumption rather than turning one incidental transitive-resolution outcome into a public behavioral promise. -`RasterGraphics` and `PersistentRasterGraphics` are intentionally distinct. Verified Sixel can satisfy ordinary raster display but does not imply terminal-resident persistent resource ownership. The persistent capability is verified only through the reviewed Kitty Graphics path and uses the same side-effect-free inspection / explicit bounded verification model introduced in 1.10. +## 10. Backend-neutral public contracts -`CreateRasterResourceAsync(...)` does not hide a new background probe. It requires current verified persistent-raster capability and a usable endpoint before committing upload traffic. +Stable public APIs describe terminal semantics rather than internal protocol choices wherever practical. -## 10. Correlation and response ownership +The following remain implementation details rather than compatibility promises: -The one-reader/query ownership model is a stable 1.x behavioral contract. +- Kitty/Sixel backend routing scores; +- raw APC/DCS control dictionaries; +- private image numbers/image ids/placement ids; +- internal registry ordering; +- table representation used for TermInfo semantic evidence; +- concrete parser/helper class names not exposed publicly. -A correlated response is transaction-owned but remains untrusted. Matching identifiers do not bypass grammar, size, termination, or overflow checks. Identified malformed/oversized traffic is recovered boundedly rather than leaked back into ordinary application input. +A public semantic operation may be implemented by one reviewed backend today without exposing that backend as caller-controlled policy. -Version 1.9 extends the same ownership principle to unsolicited semantic reports. Active query ownership remains first; semantic ownership is second; ordinary input decoding follows. After bounded semantic recovery, routing restarts at query precedence. +## 11. Deliberate non-promises -Version 1.10 verification reuses this established query/ownership machinery and does not introduce a second reader or generic raw probe API. +Stable 1.x does not promise: -Version 1.11 resource creation and acknowledged placement mutation reuse the same authoritative query ownership. Correlation includes the private image number/image id and, where relevant, the private placement id. A well-formed terminal `ENOENT` for a resource/placement believed current invalidates that local terminal-resident certainty and is surfaced as controlled `Unavailable`; other well-formed negative replies remain controlled failures rather than trusted statements about unrelated state. +- generic raw vendor command/event dispatch; +- terminal-brand-based support truth; +- automatic graphics replay/re-upload after lifecycle invalidation; +- Sixel emulation of persistent resource ownership; +- caller-selected persistent raster backend; +- public Kitty protocol identities; +- retained persistent source-image cache; +- relative placement graphs or parent placement identities; +- absolute screen-coordinate layout owned by `Icod.Terminal`; +- Unicode placeholder/virtual placements; +- animation/frame lifecycle; +- scene-graph/cells/windows/damage/layout ownership; +- image-file decoding/transcoding; +- PTY/ConPTY process hosting inside this package. -## 11. Resource-bound compatibility +Version 1.12 source rectangles and z-order are deliberately narrow additions and must not be interpreted as promises for these excluded features. -Documented resource ceilings are part of the safety contract. Implementations may become more efficient, but minor/patch releases must not silently remove bounds and introduce unbounded work or retention. +## 12. Release qualification -Raster ceilings include: +A stable release candidate is accepted only after the exact head passes: ```text -maximum dimension 16,384 -maximum pixel count 16 Mi -maximum owned pixel data 64 MiB -maximum indexed palette 256 entries +Runtime Windows +Runtime Linux +Runtime macOS +Package candidate / public API freeze +Package Foundation +Package Presentation +Package Semantic and hardening +Package Stable 1.x release line +Validated package artifact ``` -Persistent-raster ownership adds these session-local ceilings: - -```text -maximum live persistent resources 256 -maximum live persistent placements 4096 -placement Columns / Rows 1..16384 when supplied -``` - -The persistent registries are local bookkeeping limits, not claims about terminal storage quota. Exhaustion returns controlled `Unavailable` before protocol output rather than creating unbounded local state. - -Other stable bounds include the 4096-byte normal response frame, 4096-byte Kitty Base64 image-data chunk, bounded control-family frames, fixed Sixel histogram, bounded resynchronization state, and bounded semantic-event/application-event buffering. - -Interactive Kitty notification buttons remain bounded to 16 labels, 512 UTF-8 bytes per label, and 2,048 UTF-8 bytes for the combined button payload including separators. - -Increasing a ceiling may be compatible when semantics remain unchanged; decreasing a ceiling so previously supported values are rejected requires explicit compatibility review. - -## 12. Target frameworks and operating systems - -The stable 1.x package targets: - -```text -net8.0 -net9.0 -net10.0 -``` - -All three are first-class package targets. Dropping one is compatibility-sensitive and requires an explicit maintenance/security/toolchain justification and release documentation. - -The built-in `SystemTerminalControlProvider` provides native behavior for Windows, Linux, and macOS. Other operating systems receive controlled unsupported results from the built-in provider rather than fabricated POSIX/Windows behavior. - -Custom hosts remain possible through `ITerminalControlProvider`, `ITerminalInput`, and `ITerminalOutput`. - -## 13. Architecture compatibility - -Permanent layer boundaries remain part of the support model: - -- `Icod.TermInfo` owns immutable capability information; -- `Icod.Terminal` owns the live terminal conversation, query/evidence model, capability planning, unsolicited semantic-event routing, semantic output, ephemeral raster routing, persistent raster resource/placement ownership, and reversible session mechanics; -- `Icod.DCurses` owns higher-level virtual-screen/curses presentation policy; -- PTY/process hosting remains orthogonal. - -Persistent Kitty Graphics does not move virtual-screen/scene ownership into `Icod.Terminal`. Opaque resources/placements are terminal-resident ownership handles, not cells, windows, layers, or a scene graph. Unsolicited semantic events do not make the library a generic vendor-event bus. Capability planning does not make the internal backend registry, evidence ledger, or `Icod.TermInfo` provenance part of the public contract. - -## 14. Security compatibility - -Security boundaries are compatibility commitments. - -Stable 1.x does not quietly introduce through a minor/patch release: - -- generic raw OSC/CSI/DCS/APC/vendor dispatch as the ordinary API; -- hazardous host-affecting OSC 9 commands; -- generic raw OSC 633/777/1337/99 dispatch replacing reviewed semantic surfaces; -- arbitrary public Sixel/Kitty writers merely because internal grammars exist; -- public Kitty numeric image ids/image numbers/placement ids as semantic graphics identity; -- a generic raw unsolicited-event stream or arbitrary vendor-event dictionary; -- terminal-brand-triggered activation presented as capability truth; -- a competing protocol-specific input reader; -- automatic clipboard reads; -- hidden shell/environment metadata capture; -- authentication claims for terminal-supplied notification interaction reports; -- automatic image-file decoding or network/process side effects in raster display; -- hidden file/temp-file/shared-memory graphics transport; -- hidden persistent-raster source-image caching or automatic replay after lifecycle uncertainty; -- silent compositing of unsupported fractional-alpha raster data; -- cancellation-driven truncation of already-committed graphics transfers; -- automatic retry/backend switch after partial committed graphics output; -- hidden/background capability verification behind ordinary inspection; -- public capability evidence that exposes backend/dependency provenance as trusted identity. - -New security-sensitive semantic features require explicit typed API, bounded validation, documentation, and tests. - -## 15. Dependency compatibility policy - -`Icod.Terminal.csproj` is the package authority for direct NuGet dependency requirements. - -Active tests, samples, package smoke consumers, and auxiliary verification tools do not independently pin exact `Icod.TermInfo`, `Icod.Timing`, or other transitive runtime dependency versions merely to duplicate package metadata. Successful restore/build against the declared package graph is the dependency-compatibility witness unless a concrete incompatibility is under investigation. - -Package verification may assert dependency identity and package shape without turning one resolved transitive version into a second behavioral contract. - -Historical release/tranche documents may retain exact dependency versions as evidence of what was shipped at that time. - -## 16. Direct consumers and Icod.DCurses - -Direct consumers should use `TerminalSession` when they need live terminal/session mechanics without a curses virtual-screen model. - -Applications needing windows/cells/diff/refresh should normally use `Icod.DCurses` and allow that layer to own the supplied session according to its integration contract. - -Persistent raster resources/placements are appropriate building blocks for higher-level consumers, but layout, damage tracking, clipping policy, and virtual-screen/scene decisions remain higher-level responsibilities. - -Do not create independent state-owning sessions over the same physical terminal merely to divide responsibilities. - -## 17. Deprecation policy - -When an existing 1.x API can be replaced compatibly, deprecation is preferred before removal. - -Ordinary removal should identify a replacement, document migration, mark the old surface obsolete where practical, preserve it through a reasonable migration interval, and remove it only in a major release. - -Exceptional removal without a normal deprecation period is reserved for cases such as active security vulnerability or an impossible-to-support contract and still requires explicit release documentation. - -## 18. Compatibility evidence - -A release is not considered compatible merely because unit tests pass. - -The repository maintains layered evidence including: - -- retained historical public API fingerprints plus the authoritative current 1.11 fingerprint; -- Windows/Linux/macOS runtime/source validation; -- exact multi-TFM API snapshot agreement; -- fresh NuGet-only consumers for newly added or compatibility-critical semantic APIs; -- generated XML documentation verification; -- retained historical package consumers/contracts; -- current `Icod.DCurses` package-boundary integration/ownership tests; -- repeated ownership/disposal/lifecycle hardening; -- exact protocol regression vectors; -- resource-bound tests; -- release/distribution validation on configured architectures. - -Version 1.9 qualified semantic-event ownership and interactive Kitty notification reporting. Version 1.10 qualified side-effect-free semantic capability inspection, explicit bounded verification, lifecycle invalidation, concurrency/cancellation, package-only consumption, loose dependency coupling, and downstream compatibility. Version 1.11 qualifies persistent-raster acknowledgement/correlation, bounded resource/placement ownership, placement replacement, deterministic disposal, lifecycle invalidation/no-replay behavior, adversarial terminal replies, package-only consumption/XML documentation, a protocol-neutral sample, and current DCurses acceptance. - -Exact release qualification evidence belongs to the relevant pull-request workflow, merged `main` workflow, release notes, and GitHub Release rather than being hard-coded permanently into this policy document. +The Stable 1.x package shard includes fresh package-only consumption and downstream acceptance/hardening where defined by the repository release contract. -## 19. Release rule +After closure-only documentation changes, the same exact-head matrix is run again before the PR is marked ready for review. -A green feature checkpoint is not publication authorization. +## 13. Maintainer release actions -For every stable release: +PR qualification does not itself merge, tag, create a GitHub Release, or publish NuGet packages. -1. one unchanged final pull-request head must pass the complete Staging qualification matrix; -2. only that qualified exact head may be considered ready for merge; -3. merge remains an explicit maintainer action; -4. the resulting `main` head must pass Release distribution validation; -5. `v` tagging/publication remains a separate explicit maintainer action and must use the curated `docs/releases/.md` notes. +For 1.12, the maintainer/release workflow remains responsible for: -These gates may evolve operationally, but equivalent compatibility evidence must exist before historical checks are removed. +1. merging the qualified PR; +2. validating the mainline Release workflow; +3. creating/pushing `v1.12.0` only after mainline validation succeeds; +4. creating the GitHub Release and publishing NuGet through the established release workflow. diff --git a/docs/Persistent-Raster-Ownership.md b/docs/Persistent-Raster-Ownership.md index c00bbe8e0..d6ca4790e 100644 --- a/docs/Persistent-Raster-Ownership.md +++ b/docs/Persistent-Raster-Ownership.md @@ -1,8 +1,8 @@ # Persistent Raster Ownership -This document is the permanent 1.x authority for `Icod.Terminal` persistent terminal-resident raster resources and placements introduced in version `1.11.0`. +This document is the permanent 1.x authority for `Icod.Terminal` persistent terminal-resident raster resources and placements. Version 1.11 established the ownership domain; version 1.12 adds bounded source-pixel cropping and signed z-order without changing the underlying ownership/lifecycle model. -Historical C110–C119 tranche documents explain how the design was developed and qualified. This document defines the supported semantic contract consumers should rely on. +Historical tranche and versioned-roadmap documents explain how the design was developed and qualified. This document defines the supported semantic contract consumers should rely on. ## 1. Scope @@ -19,33 +19,23 @@ CreateRasterResourceAsync(...) -> deterministic disposal ``` -Persistent ownership is not a scene graph, virtual screen, window system, or image database. `Icod.DCurses` remains responsible for cells, windows, clipping/layout policy, damage, and refresh strategy. +Persistent ownership is not a scene graph, virtual screen, window system, image database, or layout engine. `Icod.DCurses` remains responsible for cells, windows, clipping/layout policy, damage, and refresh strategy. ## 2. Semantic capability -Persistent raster ownership is represented by: +Persistent ownership is represented by: ```text TerminalCapability.PersistentRasterGraphics = 9 ``` -It is intentionally distinct from ordinary `RasterGraphics`. +It is intentionally distinct from ordinary `RasterGraphics`. A terminal may support ephemeral raster output without supporting persistent terminal-resident ownership. -```text -RasterGraphics - may be usable through verified Kitty Graphics or verified Sixel - -PersistentRasterGraphics - is usable only through the reviewed persistent-capable Kitty Graphics path -``` - -A terminal may therefore support ordinary raster output without supporting this ownership domain. - -`TerminalSession.InspectCapability(...)` remains side-effect free. `TerminalSession.VerifyCapabilityAsync(...)` is explicit and may issue only the reviewed bounded support probe for this semantic capability. +`InspectCapability(...)` remains side-effect free. `VerifyCapabilityAsync(...)` is explicit and may issue only the reviewed bounded support probe for this semantic capability. ## 3. Public ownership surface -The public resource model is opaque: +Resource creation is opaque: ```csharp TerminalControlResult result = @@ -60,64 +50,134 @@ A resource can create one or more placements: TerminalControlResult result = await resource.CreatePlacementAsync( new TerminalRasterPlacementOptions { - Columns = 24 + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 320, + 180 + ), + Columns = 40, + ZIndex = -1 } ); await using TerminalRasterPlacement placement = result.GetRequiredValue(); ``` -A placement can be replaced at the current cursor location while preserving the same private placement identity: +A placement can be replaced at the current cursor location while retaining the same private placement identity: ```csharp TerminalControlMutationResult result = await placement.UpdateAsync( new TerminalRasterPlacementOptions { - Columns = 16, - Rows = 8 + SourceRectangle = new TerminalRasterSourceRectangle( + 20, + 10, + 300, + 160 + ), + Columns = 32, + Rows = 16, + ZIndex = 2 } ); ``` The public types do not expose Kitty image ids, image numbers, placement ids, raw APC command dictionaries, or a backend selector. -## 4. Placement size and position +## 4. Placement position and cell extent -`TerminalRasterPlacementOptions.Columns` and `.Rows` are nullable `int` values. +`Columns` and `Rows` are nullable `int` values. -- `null` means use protocol/default behavior for that dimension; +- `null` means protocol/default behavior for that dimension; - each supplied value must be in `1..16384`; - either dimension may be supplied independently; -- the backend derives an unspecified dimension while preserving image aspect ratio where the protocol supports that behavior. +- the backend may derive an unspecified dimension where its protocol supports that behavior. -Placement position is the terminal's current cursor location. `Icod.Terminal` does not add public pixel-coordinate or absolute-cell-coordinate placement state in 1.11. - -The reviewed backend requests placement without moving the text cursor. +Placement position is the terminal's current cursor location. Persistent placement does not move the text cursor. To reposition an existing placement, move the terminal cursor through ordinary terminal semantics and call `UpdateAsync(...)`. -## 5. Resource creation and acknowledgement +Version 1.12 does not add public absolute-cell or screen-pixel placement coordinates. + +## 5. Source rectangle — 1.12 + +`TerminalRasterSourceRectangle` selects a bounded region of the owned source raster in source-image pixel coordinates. + +```csharp +TerminalRasterSourceRectangle rectangle = new( + x, + y, + width, + height +); +``` + +Contract: + +- `X` and `Y` are zero-based and non-negative; +- `Width` and `Height` are positive; +- scalar values respect `TerminalRasterImage.MaximumDimension`; +- the right and bottom edges must not exceed the actual source resource dimensions; +- `SourceRectangle == null` means the full source image; +- a present rectangle is emitted as all four crop fields together rather than partially; +- validation occurs before placement output commitment. + +Resource bookkeeping therefore retains immutable source width/height metadata. It does **not** retain source pixel data. + +Source cropping selects source pixels only. It does not define terminal screen position, relative placement graphs, or clipping/layout policy for `Icod.DCurses`. + +## 6. Signed z-order — 1.12 + +`TerminalRasterPlacementOptions.ZIndex` is nullable signed `int`. + +- every value from `int.MinValue` through `int.MaxValue` is accepted; +- negative values are preserved; +- `null` means backend/default stacking order; +- wire formatting uses invariant signed decimal representation. + +Z-order is relative stacking intent for one placement. It does not create parent/child placement identity, graph lifetime ownership, cycle detection, or a general scene-composition model. + +## 7. Complete replacement semantics + +`UpdateAsync(...)` replaces the placement at the current cursor using the newly supplied options. It is not a patch against prior `TerminalRasterPlacementOptions`. + +A caller that wants to retain a prior crop, extent, or z-order must supply it again in the update options. + +## 8. Resource creation and acknowledgement Persistent resource creation is acknowledged before the library publishes a usable public handle. -Internally, the session allocates a private nonzero image number for upload correlation. A successful acknowledgement must correlate that image number and return a nonzero terminal-assigned image id. Future placement and cleanup operations use that private terminal image identity. +Internally, the session allocates a private nonzero image number for upload correlation. A successful acknowledgement must correlate that image number and return a nonzero terminal-assigned image id. Future placement and cleanup operations use that private identity. + +If upload cannot establish reliable acknowledgement, no public resource is returned. The library does not publish an object whose terminal-side existence is ambiguous. -The public handle contains no protocol identity that callers can manufacture or reuse. +## 9. Placement acknowledgement and shared transaction -If upload cannot establish a reliable acknowledgement, no public resource is returned. The library does not publish an object whose terminal-side existence is ambiguous. +Placement creation and update reuse the existing authoritative query/input architecture. There is no graphics-specific reader. + +A placement acknowledgement belongs to the operation only when the expected private terminal image id and placement id correlate. Wrong identities do not satisfy another operation. + +Correlated responses remain untrusted. Framing, numeric overflow, duplicate fields, response size, and semantic status are validated before success is accepted. + +The reviewed deterministic persistent placement encoding is: + +```text +Ga=p,i=,p=,C=1[,x=...[,y=...[,w=...[,h=...]]]][,c=...][,r=...][,z=...] +``` -## 6. Placement acknowledgement and query ownership +Create and update share this encoder/transaction. Existing 1.11 bytes remain unchanged when `SourceRectangle` and `ZIndex` are omitted. -Placement creation and update reuse the existing authoritative query/input architecture. There is no graphics-specific input reader. +## 10. Timeout and late-response ownership -A placement acknowledgement is owned by the active transaction only when the expected terminal image id and placement id correlate. Wrong identities do not satisfy another operation. +Persistent placement queries use the same bounded query manager as other terminal requests. -Correlated terminal responses remain untrusted. The implementation validates framing, numeric overflow, duplicate fields, and bounded response sizes before accepting semantic success. +A timeout does not allow a stale acknowledgement from an earlier placement identity to complete a later operation. A later request may proceed according to ordinary query scheduling, but only a response correlated to that later private image/placement identity may satisfy it. -Malformed correlated responses are failures rather than capability evidence. +Late-response handling remains bounded and does not create a second graphics-specific input path. -## 7. Cancellation and committed output +## 11. Cancellation and committed output -Arguments, options, current ownership, endpoint/capability state, and caller cancellation are checked before commitment where possible. +Arguments, options, ownership, endpoint/capability state, and caller cancellation are checked before commitment where possible. The output gate may be cancelled before the first frame commits. Once persistent graphics output commits, ordinary caller cancellation does not intentionally truncate the logical transaction. @@ -125,7 +185,7 @@ After partial committed output, the library does not blindly replay, switch to S A transport failure remains a transport failure and is surfaced to the caller. -## 8. Bounded ownership +## 12. Bounded ownership Session bookkeeping is bounded independently of the terminal's own storage limits: @@ -136,42 +196,40 @@ maximum live persistent placements 4096 A full local registry returns controlled `Unavailable` before protocol output. -Private resource/image-number and placement identities are nonzero, avoid live collisions, allocate monotonically where possible, and have explicit wraparound handling. +Private identities are nonzero, avoid live collisions, allocate monotonically where possible, and have explicit wraparound handling. -The registry stores ownership/lifecycle bookkeeping only. After successful resource creation, the library does not retain an arbitrary hidden copy of the source raster image. +The registry stores ownership/lifecycle metadata only. After successful resource creation, the library does not retain an arbitrary hidden copy of the source raster image. -## 9. Generation-scoped certainty +## 13. Generation-scoped certainty Persistent terminal identities are valid only for the session generation in which they were established. -The following invalidate existing terminal-resident certainty: +The following invalidate current terminal-resident certainty: - explicit `TerminalSession.InvalidateState()`; - managed suspend/resume generation changes; -- other session lifecycle transitions that invalidate terminal state knowledge. +- other lifecycle transitions that invalidate terminal state knowledge. After invalidation: - existing resource and placement handles are stale; - placement update returns controlled `Unavailable` before output; - new placement creation from a stale resource returns controlled `Unavailable` before output; -- disposal releases local ownership but does not emit stale numeric identifiers; -- the library does not automatically re-upload or rebind the raster; -- no hidden source-image cache is consulted because 1.11 does not retain one for replay. - -A caller that needs persistent graphics again must establish current capability and create a new resource explicitly. +- disposal releases local ownership without emitting stale numeric identifiers; +- no automatic re-upload or rebind occurs; +- no hidden source-image cache is consulted. -## 10. Terminal eviction and `ENOENT` +## 14. Terminal eviction and `ENOENT` -Terminal-resident image storage is external state. The terminal may evict resources according to its own quota or policy even while the local handle remains otherwise current. +Terminal-resident graphics storage is external state. The terminal may evict resources according to its own quota or policy even while the local handle is otherwise current. -A well-formed correlated Kitty Graphics `ENOENT` response for a resource/placement the session believed current invalidates that ownership certainty. The operation returns controlled `Unavailable` semantics and later operations do not emit stale identifiers. +A well-formed correlated `ENOENT` response invalidates the library's certainty for the affected resource/placement. The operation returns controlled `Unavailable`, and later operations do not emit stale identifiers. -Other well-formed negative terminal responses remain controlled `Failed` results with bounded diagnostic text where applicable. +Other well-formed terminal-negative responses remain controlled failures. -This behavior is not automatic replay. The caller remains responsible for deciding whether to create a new resource. +This is not automatic replay. The caller decides whether to establish capability and create a new resource. -## 11. Placement disposal +## 15. Placement disposal Placement disposal is locally idempotent. @@ -185,7 +243,7 @@ For a current placement, the first disposal: A stale placement performs local cleanup only. -## 12. Resource disposal +## 16. Resource disposal Resource disposal is locally idempotent and owns its children. @@ -194,21 +252,21 @@ For a current resource, disposal: 1. prevents new child placements; 2. closes/releases child placement ownership; 3. attempts child placement deletion before resource-data deletion; -4. attempts the terminal resource-data delete; +4. attempts terminal resource-data deletion; 5. releases local ownership even when cleanup transport fails; -6. aggregates multiple cleanup transport failures where necessary. +6. aggregates multiple cleanup failures where necessary. -Disposing an already stale resource emits no stale terminal identifiers. +An already-stale resource emits no stale terminal identifiers. -## 13. Session teardown +## 17. Session teardown Current-generation persistent graphics participate in ordinary session teardown. -The session drains committed transactions, deletes current placements before current resource data, aggregates persistent cleanup failures with the existing cleanup/restoration model, and then continues final terminal restoration according to the broader session contract. +The session drains committed transactions, deletes current placements before current resource data, aggregates persistent cleanup failures with the existing restoration model, and then continues final terminal restoration. -If persistent state was already invalidated, teardown performs local ownership cleanup only. +Already-stale persistent state receives local-only cleanup. -## 14. Security and privacy boundary +## 18. Security and privacy boundary Persistent raster traffic is external terminal I/O. @@ -219,60 +277,41 @@ Stable guarantees include: - bounded image dimensions/storage inherited from `TerminalRasterImage`; - bounded resource/placement registries; - bounded correlated response parsing; +- source-rectangle validation against immutable source dimensions before output; - direct Kitty transfer only; - no file, temporary-file, or shared-memory transport chosen silently; - no retained arbitrary source-image cache after creation; - no hidden replay after lifecycle uncertainty or partial commitment; - correlation establishes routing ownership, not terminal authenticity. -A successful acknowledgement proves only that a well-formed correlated response was received under the protocol contract. It does not authenticate the emulator, multiplexer, remote endpoint, host, desktop session, or user. +A successful acknowledgement proves only that a well-formed correlated response was received under the protocol contract. It does not authenticate the terminal, multiplexer, remote endpoint, host, desktop session, or user. -## 15. Backend neutrality +## 19. Backend neutrality -The persistent public surface intentionally speaks in resource and placement semantics rather than Kitty protocol vocabulary. +The public surface speaks in resource, source rectangle, cell extent, z-order, and placement semantics rather than Kitty protocol vocabulary. -Version 1.11 does not emulate persistence through Sixel. Such emulation would require the library to retain and redraw image data and would change the ownership/lifecycle contract substantially. +Persistent ownership is not emulated through Sixel. Such emulation would require retaining/redrawing image data and would materially change lifecycle ownership. -The implementation may use Kitty Graphics internally, but callers should plan against `PersistentRasterGraphics`, not terminal brand, `TERM`, APC framing, or numeric image identifiers. +The implementation may use Kitty Graphics internally, but callers plan against `PersistentRasterGraphics`, not terminal brand, `TERM`, APC framing, or numeric image identities. -## 16. Explicit exclusions +## 20. Explicit exclusions after 1.12 -The 1.11 persistent ownership contract does not include: +The persistent ownership contract does not include: - public backend ids or Kitty numeric identities; - caller-selected graphics backend; - Sixel persistent-resource emulation; - automatic replay/re-upload/rebind; - retained source-image cache; -- source rectangles; -- z-order; -- Unicode placeholder placement; -- relative placement; -- pixel-coordinate placement; -- animation; +- Unicode placeholder/virtual placement; +- relative placement or parent placement identity; +- placement chains / graph cycle handling; +- absolute screen-pixel placement or Terminal-owned layout; +- animation/frame lifecycle; - scene-graph ownership; - image-file decoding/transcoding; - Kitty file/temp-file/shared-memory transfer; - PTY/ConPTY hosting; - cells/windows/damage/layout ownership. -Later releases may add narrowly reviewed functionality without weakening this base ownership contract. - -## 17. Sample and related authorities - -A focused backend-neutral example is available at: - -```text -samples/Icod.Terminal.PersistentRaster.Sample/ -``` - -Related permanent authorities are: - -- `Architecture.md`; -- `Security-and-Privacy.md`; -- `Compatibility-and-Versioning.md`; -- `Capability-Inspection-and-Planning.md`; -- `Terminal-Session-and-Ownership.md`; -- `Lifecycle-and-Restoration.md`. - -The final 1.11 API surface is frozen by `Public-API-Baseline-1.11.md` and its SHA-256 fingerprint. +Source rectangles and signed z-order are supported as of 1.12; they are deliberately bounded additions to one placement rather than entry points to the excluded scene-graph features. diff --git a/docs/Public-API-Baseline-1.12.md b/docs/Public-API-Baseline-1.12.md index 40e5e08aa..d08b2f0d3 100644 --- a/docs/Public-API-Baseline-1.12.md +++ b/docs/Public-API-Baseline-1.12.md @@ -1,14 +1,14 @@ # Icod.Terminal Public API Baseline — 1.12.0 **Release:** `1.12.0` -**Status:** development API freeze after T123 +**Status:** final stable API freeze **Target frameworks:** `net8.0`, `net9.0`, `net10.0` ## Purpose -This document records the reviewed additive public surface planned for `Icod.Terminal 1.12.0` after T122 source rectangles and T123 signed z-order. The historical 1.11 baseline remains unchanged as compatibility evidence. +This document records the reviewed additive public surface for `Icod.Terminal 1.12.0`. The historical 1.11 baseline remains unchanged as compatibility evidence. -No further public API additions are planned for T124–T127. Later tranches implement, harden, document, package, and qualify this surface. +No public API was added after the T123 freeze. T124–T127 implemented, hardened, documented, packaged, and qualified this exact surface. ## Public additions over 1.11 @@ -47,7 +47,7 @@ No new `TerminalCapability` value, public backend selector, Kitty identity, raw The deterministic reflection snapshot is identical across `net8.0`, `net9.0`, and `net10.0`. -After normalizing line endings to LF, the reviewed 1.12 development fingerprint is: +After normalizing line endings to LF, the final reviewed 1.12 fingerprint is: ```text eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8 @@ -63,4 +63,4 @@ The machine-readable fingerprint is stored in: The 1.12 surface is additive over the stable `1.0.0` compatibility floor and preserves all existing public signatures and enum numeric values. Existing 1.11 placement behavior remains unchanged when `SourceRectangle` and `ZIndex` are omitted. -T127 must re-run the exact public snapshot and confirm this fingerprint is unchanged before stable release. Any later public API drift requires explicit review rather than silently changing this baseline. +Any later public API drift requires a separately reviewed later-release baseline rather than modifying this historical 1.12 record. diff --git a/docs/Security-and-Privacy.md b/docs/Security-and-Privacy.md index 9cf2d0bea..ec5d44d11 100644 --- a/docs/Security-and-Privacy.md +++ b/docs/Security-and-Privacy.md @@ -11,40 +11,27 @@ This document defines the permanent 1.x security and privacy boundary. - application-supplied arguments may be untrusted; - terminal input, unsolicited semantic events, capability responses, graphics acknowledgements, and query responses are external input and may be malformed or adversarial; - the attached terminal, multiplexer, remote session, or transport may fabricate otherwise well-formed observations; -- the terminal may not implement a protocol exactly as expected; - successful byte transmission does not prove terminal-side support or application; -- even an acknowledged terminal-resident graphics identity is terminal-controlled state rather than an authentication primitive; -- terminal metadata may be logged, persisted, forwarded, surfaced to the desktop, or visible to other software depending on the environment; -- large raster inputs, large terminal replies, and terminal-resident graphics bookkeeping may create accidental or adversarial resource pressure. +- acknowledged persistent graphics identity remains terminal-controlled external state rather than an authentication primitive; +- metadata and raster content may be logged, persisted, forwarded, recorded, or exposed outside the process; +- large raster inputs, replies, and terminal-resident ownership bookkeeping can create resource pressure. -The library therefore favors typed semantic APIs, bounded parsing/encoding, pre-output validation, explicit capability evidence, opaque ownership handles, and one authoritative input/query/event path over raw generic protocol construction. +The library therefore favors typed semantic APIs, bounded parsing/encoding, pre-output validation, explicit capability evidence, opaque ownership handles, and one authoritative input/query/event path over generic raw protocol construction. ## 2. Control-sequence injection boundary -Text-bearing semantic protocols validate payloads according to their relevant protocol before commitment where possible. +Text-bearing semantic protocols validate payloads according to their reviewed grammar before commitment where possible. Where raw controls are not semantic data, C0/DEL/C1 controls are rejected so caller text cannot inject unrelated terminal sequences. -Where raw control characters are not meaningful semantic data, the library rejects C0, DEL, and C1 controls so caller text cannot inject BEL, ESC, OSC/ST, or another terminal sequence. +Examples of reviewed encoding boundaries include strict UTF-8 percent encoding, Base64 where required by protocol, closed metadata grammars, and typed color/pointer/keyboard/notification/raster APIs. -Different protocols use different safe encodings. Examples include: +Kitty Graphics raw image bytes use protocol-defined Base64 within a typed bounded APC dialect. Persistent raster APIs expose opaque resources/placements plus typed placement options rather than caller-supplied control dictionaries or numeric protocol identities. -- OSC 7 path data uses strict UTF-8 percent encoding; -- OSC 52 binary payloads use Base64; -- OSC 99 text/icon/button data uses protocol-defined Base64 and closed metadata grammar; -- OSC 133 and OSC 633 metadata use reviewed serializers/escaping; -- OSC 777 rejects delimiters/control bytes for which the protocol defines no interoperable escape; -- OSC 1337 user-variable values use strict UTF-8 plus Base64; -- Kitty Graphics raw image bytes use protocol-defined Base64 inside a typed bounded APC dialect; -- closed color, pointer, keyboard, notification-event, capability-planning, and raster APIs avoid arbitrary caller-supplied protocol strings; -- persistent raster APIs expose opaque resource/placement objects rather than caller-supplied Kitty control dictionaries or numeric ids. - -Validation protects framing integrity. It does not make semantic content confidential, authentic, or trustworthy. +Validation protects framing integrity. It does not make content confidential, authentic, or trustworthy. ## 3. Bounded resources Input decoding, paste handling, query transactions, unsolicited semantic reports, application-event buffering, request/response frames, late-response ownership, resynchronization, capability verification, graphics processing, and persistent-raster bookkeeping are bounded. -The normalized control-language layer uses one bounded scanner for CSI, DCS, OSC, APC, PM, and SOS rather than separate unbounded per-dialect accumulators. - Public raster ceilings remain: ```text @@ -54,7 +41,7 @@ maximum owned pixel bytes 64 MiB maximum indexed palette 256 entries ``` -Persistent-raster session bookkeeping is additionally bounded: +Persistent-raster session bookkeeping remains: ```text maximum live persistent resources 256 @@ -62,34 +49,17 @@ maximum live persistent placements 4096 placement Columns / Rows 1..16384 when supplied ``` -Relevant protocol/resource ceilings include: - -```text -normal terminal response frame 4,096 bytes -small complete DCS frame 4,096 bytes -small complete APC frame 8,192 bytes -Kitty Base64 image data per APC chunk 4,096 bytes -Sixel quantizer histogram 32 x 32 x 32 bins -Kitty notification buttons 16 labels -Kitty button label 512 UTF-8 bytes -Kitty combined button payload 2,048 UTF-8 bytes including separators -``` +Version 1.12 adds source rectangles without increasing source raster ceilings. Rectangle scalar values are bounded, dimensions must be positive, coordinates non-negative, and the complete rectangle must fit inside the owning resource before output. Signed z-order consumes only a bounded `int` value and adds no unbounded layer registry. -Sixel output is generated as bounded lazy segments. Kitty Graphics direct output is generated as bounded lazy Base64/application chunks and one bounded APC frame at a time. Large graphics do not require one complete encoded transfer in memory. +Relevant framing/resource ceilings remain bounded, including normal response frames, APC/DCS frames, Kitty Base64 chunk data, notification metadata, and Sixel quantization state. -Persistent resource/placement registry limits are local safety bounds, not claims about terminal storage quota. Registry exhaustion produces controlled `Unavailable` before protocol output rather than unbounded local growth. - -Malformed or oversized owned query/semantic/graphics-acknowledgement traffic is recovered through bounded drain/resynchronization rather than unbounded accumulation or leakage back into ordinary application text. +Registry exhaustion returns controlled `Unavailable` before protocol output rather than unbounded local growth. ## 4. One authoritative input reader A live `TerminalSession` owns the authoritative input decoder, query router, and unsolicited semantic-event classifier. -The stable 1.x surface does not expose a session raw-input property. A competing raw read could steal bytes from UTF-8 scalars, key sequences, paste frames, lifecycle traffic, unsolicited semantic reports, active query responses, or persistent-raster acknowledgements. - -`ITerminalInput` remains public for custom transport injection, but a caller supplying the transport must not create a competing reader while the session owns it. - -The routing order is: +The stable routing order is: ```text active query response @@ -97,268 +67,148 @@ active query response -> ordinary application input ``` -A query-owned response is never also published as a semantic event. A recognizable unsolicited report does not satisfy an unrelated query merely because both use a shared control family. - -Sixel capability observation, Kitty Graphics support probing, capability verification, and 1.11 persistent-raster acknowledgements all reuse the same authoritative input/query path. None introduces a protocol-specific reader, notification reader, capability reader, graphics reader, or callback stream. - -## 5. Capability evidence is not terminal identity - -`TERM`, terminal names, environment variables, host operating system, known emulator brands, registry membership, and caller backend preference are context, not sufficient proof that a live protocol is supported. - -The internal model separates semantic operation, backend, support state, evidence source, and endpoint availability. - -Static terminal-description evidence and generation-scoped live evidence are distinct. Timeout or silence is not automatically `Unsupported`, and caller cancellation is not negative capability evidence. - -For Sixel, Primary DA attribute `4` may provide positive support evidence. A valid Primary DA response without `4` does not automatically prove terminal-wide Sixel unsupported. - -For Kitty Graphics, a correlated valid Kitty response verifies the backend; the reviewed Primary DA barrier arriving first provides negative evidence for that concrete probe; timeout before either authoritative result remains unknown. - -The distinction prevents both false-positive brand guessing and false-negative interpretation of ordinary silence. - -## 6. Public capability planning security boundary — 1.10+ - -Version 1.10 exposes semantic capability planning through: - -```text -TerminalSession.InspectCapability(...) -TerminalSession.VerifyCapabilityAsync(...) -TerminalCapabilityStatus -``` - -`InspectCapability(...)` is side-effect free. It reads only the session's existing in-memory knowledge and emits no terminal bytes. Applications may therefore use inspection for ordinary planning without triggering hidden terminal fingerprinting. - -`VerifyCapabilityAsync(...)` is deliberately explicit because verification may emit bounded terminal query/probe traffic. Version 1.10 introduced reviewed live probe paths for `KeyboardReporting` and `RasterGraphics`; version 1.11 adds `PersistentRasterGraphics` using only the reviewed Kitty Graphics probe path. Capabilities without a reviewed live probe remain inspection-only. - -A successful `Verified` result means that the reviewed terminal observation established current-generation support under the library contract. It is **not** authentication of the terminal, emulator, multiplexer, desktop session, host, or user. - -Public capability evidence intentionally exposes only: - -```text -None -StaticDescription -LiveObservation -``` - -It does not expose raw protocol frames, backend identities, routing scores, arbitrary terminfo capability names, `Icod.TermInfo` objects, or terminal-brand heuristics. This is both an abstraction boundary and a security/privacy boundary: consumers should not accidentally treat implementation provenance or terminal branding as trusted identity. - -Support and endpoint availability remain separate. An unavailable endpoint does not rewrite known terminal support to `Unsupported`, and verification does not bypass endpoint/query lifecycle restrictions merely to produce an answer. - -Pre-cancelled verification emits no probe traffic. Repeated verification of decisive current live evidence does not intentionally re-probe merely to return the same result. - -Persistent resource creation does not perform an additional hidden support probe. It requires current verified persistent-raster capability before committing upload traffic. - -## 7. Correlation grants ownership, not trust +A query-owned response is never also delivered as ordinary input/semantic event. Sixel capability observation, Kitty Graphics support probing, capability verification, and persistent-raster acknowledgements all reuse this same authority. -A response that matches an active query identity is still attacker-controlled terminal input. +No source-rectangle or z-order feature adds a graphics-specific reader or side channel. -Correlation or semantic recognition grants bounded routing ownership, not trust. Matching identifiers do not bypass validation. +## 5. Capability evidence is not identity -For the Kitty Graphics support probe, once a complete matching image id is observed in a recognizable APC prefix, that string remains transaction-owned through later malformed/aborted/oversized recovery. Numeric parsing is overflow-safe; CAN/SUB, malformed termination, oversize, and missing termination remain failures according to the bounded query contract. +`TERM`, emulator names, environment values, host OS, registry membership, or caller backend preference are context rather than sufficient live support proof. -The same principle applies to unsolicited semantic notification reports. Recognition prevents hostile owned traffic from being reinterpreted as ordinary text, but does not authenticate identifiers, button numbers, close events, or event ordering. +Static description evidence and generation-scoped live evidence remain distinct. Timeout or silence is not automatically `Unsupported`; caller cancellation is not negative capability evidence. -Version 1.11 applies the same rule to persistent-raster acknowledgements. The private image number, terminal-assigned image id, and placement id are correlation fields, not security tokens. They are strictly parsed, overflow checked, duplicate-field checked, and matched to the active operation before state advances. +`InspectCapability(...)` emits no terminal traffic. `VerifyCapabilityAsync(...)` is explicit because verification may emit bounded probes. A `Verified` observation means support was established under the reviewed contract; it is not authentication of a terminal, host, desktop session, multiplexer, or user. -A well-formed terminal `ENOENT` for a resource/placement believed current invalidates the library's certainty about that terminal-resident object. It does not authenticate why the object disappeared and is not generalized into conclusions about unrelated terminal state. +Persistent resource creation requires current verified persistent-raster capability and does not perform a hidden additional support probe. -## 8. Emission is not application +## 6. Correlation grants ownership, not trust -For unacknowledged output protocols, successful completion normally means only that the requested bytes were successfully written. +A response that matches an active transaction identity is still terminal-controlled untrusted input. -It does not prove that the terminal: +Matching private image/placement ids grant bounded routing ownership only. Numeric parsing, duplicate-field rejection, grammar, termination, and response-size bounds still apply. -- supports the protocol; -- recognized the frame; -- applied the requested state; -- displayed a notification or image; -- retained an identifier or cache entry. +A stale late placement acknowledgement cannot complete a later placement with a different private identity. Timeout/late-response handling remains bounded through the existing query manager. -For explicit query APIs, successful completion means a correlated response was received and parsed according to the reviewed grammar. The response remains untrusted terminal input. +A well-formed correlated `ENOENT` invalidates the library's certainty about the affected terminal-resident object. It does not authenticate why the object disappeared or imply anything about unrelated state. -Raster display is capability-gated before emission, but successful `DisplayRasterAsync(...)` output still does not claim visual verification after bytes are written. +## 7. Emission and acknowledgement -For acknowledged persistent resource creation, successful completion means that a correlated valid terminal response accepted the reviewed upload transaction and supplied a usable terminal image identity under the current lifecycle generation. It still does not authenticate the terminal or prove how/where any future placement will be rendered. +For unacknowledged protocols, successful completion generally means requested bytes were written; it does not prove visual application. -## 9. Raster input and alpha semantics +For acknowledged persistent resource/placement operations, success means a well-formed correlated response was accepted under the protocol contract. It still does not authenticate the terminal or guarantee future persistence/visual stacking. -`TerminalRasterImage` owns an immutable snapshot of caller-provided raw raster data. Copying input at construction prevents asynchronous display from observing later caller mutation of supplied buffers. +`ZIndex` is therefore a requested relative stacking intent, not a verified global scene order. -The backend-neutral model preserves straight RGBA alpha. +## 8. Raster input and source cropping -Sixel supports only alpha semantics it can preserve truthfully. Fractional alpha remains valid raster data, but the Sixel backend returns controlled unsupported rather than silently compositing against an invented background. +`TerminalRasterImage` owns an immutable snapshot of caller-provided bounded raw raster data. -Kitty Graphics direct RGBA32 transfer preserves fractional alpha. Indexed8 input expands to RGB24 only when referenced palette colors are opaque; otherwise it expands to RGBA32 preserving indexed alpha. +Version 1.12 source rectangles select a subset of those source pixels for one placement. Resource state stores only immutable source dimensions needed to validate the rectangle; it does not retain arbitrary source pixel bytes after persistent creation for replay. -The library does not perform hidden premultiplication, gamma/profile conversion, or arbitrary background flattening. +Cropping does not fetch files, create shared memory, disclose path names, or widen external-storage attack surface. It only changes which already-owned source pixels participate in a placement. -## 10. Deterministic bounded graphics work +## 9. Deterministic bounded graphics work -Sixel quantization uses bounded deterministic work state and stable tie-breaking. High-entropy input does not create an unbounded color dictionary or optimization search. +Sixel and Kitty graphics paths retain bounded deterministic work and lazy output. The library does not silently choose file, temporary-file, or shared-memory Kitty transfer because those introduce path naming, lifetime, permissions, visibility, race, cleanup, and cross-process concerns absent from direct terminal traffic. -Kitty Graphics direct transfer (`t=d`) remains the reviewed transport. The library does not silently choose file, temporary-file, or shared-memory transfer media because those introduce path naming, lifetime, permissions, race, visibility, cleanup, and cross-process concerns absent from direct terminal traffic. +Persistent resource upload continues to use direct transfer. -Version 1.11 persistent resource upload uses that same direct-transfer policy. No file/temp-file/shared-memory shortcut is introduced merely because a resource may outlive one display operation. +Base64 is protocol framing, not encryption. -Base64 is framing-safe encoding, not encryption. +## 10. Committed graphics output -## 11. Committed graphics output +Raster and persistent-raster operations use session serialization and committed-output semantics. -Raster and persistent-raster operations use committed output semantics through the session serialization boundary. +Before commitment, validation/cancellation remain effective. After commitment, ordinary caller cancellation does not intentionally truncate a logical graphics transaction. -Before commitment, validation and caller cancellation remain effective. After the first backend frame commits, ordinary caller cancellation is not allowed to intentionally truncate the logical graphics transaction. +If transport fails after commitment, the error is surfaced. The library does not automatically replay, switch raster backends, or speculate that recovery commands are safe. -For Sixel, the committed unit is the complete DCS transaction through ST and flush. For Kitty Graphics, several complete APC frames may form one logical direct-transfer transaction and remain serialized through the final frame and flush. +Source-rectangle validation occurs before placement output commitment. Invalid create/update rectangles therefore produce no new placement output and do not acquire a new acknowledged transaction unnecessarily. -Persistent resource upload likewise remains one logical acknowledged transaction. The library does not publish a public resource until the required correlated acknowledgement establishes terminal-side identity. Placement creation/update remains serialized with the same session output authority; cleanup commands are targeted to private identities owned by the session. +## 11. Persistent raster ownership — 1.11+ -If the transport fails after commitment, the error is surfaced. The library does not automatically retry the image, replay uncertain output, switch raster backends, or speculate that additional recovery commands are safe. +The public surface exposes opaque `TerminalRasterResource` and `TerminalRasterPlacement` objects. Numeric protocol identities remain private implementation state so callers cannot forge the semantic ownership model. -Session teardown drains committed output before output-state restoration continues. +Successful resource creation retains bounded ownership/source-dimension metadata, not a hidden arbitrary source-image cache. -## 12. Persistent raster ownership — 1.11 +Persistent identities are generation scoped. `InvalidateState()` and lifecycle changes make existing identity certainty stale. Stale mutation returns controlled `Unavailable`; stale disposal releases local ownership without emitting stale numeric identifiers. -Persistent raster ownership is deliberately narrower than a scene graph. +While current, cleanup is child-first: placements are deleted before resource data. Local ownership is released even if terminal cleanup transport fails, preventing ambiguous retry ownership. -The public surface exposes opaque `TerminalRasterResource` and `TerminalRasterPlacement` objects. Numeric Kitty image ids, image numbers, and placement ids remain private implementation state so callers cannot forge ownership or collide intentionally with other terminal clients through the semantic API. +## 12. 1.12 placement options and trust boundary -A successful resource upload does not cause the library to retain the caller's raster indefinitely. After creation succeeds, the session retains bounded ownership bookkeeping, not a hidden source-image cache for automatic replay. +`TerminalRasterSourceRectangle` and `TerminalRasterPlacementOptions.ZIndex` are typed data, not raw protocol fragments. -Persistent identities are lifecycle-generation scoped. `InvalidateState()` and managed lifecycle generation changes make existing resource/placement certainty stale. Stale handles do not trigger re-upload, backend switching, or stale-id cleanup traffic. Mutations return controlled `Unavailable`; disposal releases local ownership without emitting stale numeric identifiers. +Security-relevant guarantees: -While identities are current, cleanup is child-first: placements are removed before resource data. Local ownership is released even if terminal cleanup transport fails, preventing retry loops from turning stale protocol identity into an unbounded or ambiguous ownership model. +- rectangle scalar validation is performed by constructors/options before output; +- resource-aware validation prevents a crop from escaping the uploaded raster dimensions; +- widened arithmetic prevents boundary arithmetic from overflowing before comparison; +- a present crop is encoded as one complete reviewed four-field tuple; +- z-order is formatted from a signed `int` using invariant decimal formatting; +- neither option exposes public image/placement ids or arbitrary control keys; +- neither option adds unbounded scene/layer bookkeeping. -The terminal may independently evict stored graphics data under its own quota/policy. A correlated `ENOENT` is therefore treated as loss of that terminal-resident certainty, not as an impossible condition. +Applications should not treat z-order as an authorization or visibility boundary. A terminal controls final rendering and may ignore, reinterpret, or externally compose terminal output. -## 13. No generic raw graphics or capability escape hatch +## 13. No generic raw graphics/capability escape hatch The stable semantic surface intentionally does not expose: -- a generic public DCS/Sixel writer; -- a generic public APC/Kitty Graphics writer; -- caller-selected Sixel/Kitty raster routing; -- arbitrary Kitty control-data dictionaries; +- generic public DCS/Sixel or APC/Kitty writers; +- caller-selected Sixel/Kitty routing; +- arbitrary Kitty control dictionaries; - raw capability-probe frame/matcher APIs; -- public backend-routing scores; -- arbitrary internal evidence-ledger entries; -- arbitrary terminfo capability-name probing; -- caller-selected Kitty image ids/image numbers/placement ids; -- a generic scene/layer/z-order/animation model; -- hidden persistent source-image caching or replay. +- public routing scores or arbitrary evidence-ledger entries; +- caller-selected Kitty image/image-number/placement ids; +- a generic relative-placement graph, scene engine, or animation system; +- hidden persistent source-image caching/replay. -`TerminalSession.Output` remains a public advanced borrowed transport and can be misused by a caller. Direct writes through it are outside session serialization and semantic validation; that escape hatch is not an endorsement of constructing arbitrary untrusted terminal traffic. +`TerminalSession.Output` remains an advanced borrowed transport and can be misused; direct writes through it are outside ordinary session serialization/semantic validation. -## 14. Image decoding is out of scope +## 14. Image decoding remains out of scope -`Icod.Terminal` consumes bounded raw pixel/index data. It does not decode PNG, JPEG, GIF, or other image files as part of raster display. +`Icod.Terminal` consumes bounded raw pixel/index data. It does not decode PNG/JPEG/GIF or other image files as part of raster display. This avoids importing file-parser, decompression-bomb, metadata, and color-profile attack surfaces into the core live-terminal package. -This avoids importing file-parser attack surface, metadata handling, decompression-bomb policy, color-profile interpretation, and format-specific security decisions into the core live-terminal package. +## 15. Metadata and privacy-sensitive output -Applications may decode image formats with libraries appropriate to their own trust model, then provide bounded raw raster data to `Icod.Terminal`. +Clipboard data, current-location metadata, hyperlinks, desktop notifications, shell/prompt metadata, command lines, and raster pixels may escape the application process through terminal/desktop/recording surfaces. -## 15. Clipboard privacy — OSC 52 +The caller decides whether disclosure is appropriate. `Icod.Terminal` does not automatically discover/publish filesystem paths, shell history, clipboard data, or other privacy-sensitive context merely because a terminal protocol can carry it. -Clipboard writes can place application data into terminal or desktop selection state. Clipboard reads request external selection data and are explicitly privacy-sensitive. +## 16. Terminal observations can fingerprint the environment -`ReadClipboardAsync(...)` is never called automatically by session open, capability inspection, capability verification unrelated to clipboard, graphics probing, lifecycle handling, or disposal. +Explicit queries and capability verification can reveal terminal/environment characteristics. This is why live verification is explicit and bounded rather than an automatic side effect of ordinary inspection. -Applications should treat returned clipboard bytes as untrusted external input and should not publish secrets to terminal clipboard state unintentionally. +Applications should avoid unnecessary probing when privacy or anti-fingerprinting concerns outweigh the value of stronger capability evidence. -## 16. Current-location and shell metadata disclosure +## 17. Dependency boundary -OSC 7, OSC 9;9, OSC 633 `Cwd`, OSC 1337 `CurrentDir`, and related semantic metadata can reveal user names, source-tree names, customer/project names, mount points, shares, and host identity. +Version 1.12 does not add a production dependency for image decoding, scene layout, TermInfo Inspection/Source, or any graphics toolkit. -`Icod.Terminal` does not automatically discover and publish environment/current-directory/shell-history data. The caller decides whether disclosure is appropriate. +The production package graph remains: -## 17. Hyperlink security — OSC 8 - -The library validates hyperlink framing and URI syntax but does not decide whether a URI is safe for a particular application to expose to users. - -It does not fetch targets, resolve DNS, launch browsers/shells, or apply a universal URI-scheme trust policy. - -## 18. Desktop notification privacy and trust - -OSC 9, OSC 777, and OSC 99 notifications can leave the terminal window and appear in desktop notification surfaces, logs, recordings, screen sharing, or accessibility software. - -Applications should not place secrets in notification content unless that disclosure is intended. Base64 used by OSC 99 is encoding, not encryption. - -Version 1.9 notification activation/button/close reports are validated but unauthenticated terminal-controlled input. A malicious or compromised terminal path can fabricate notification identifiers, activation events, button numbers, close events, and close-tracking results. - -An identifier is correlation data, not a capability token, trusted desktop handle, cryptographic proof, or evidence that a trusted human performed an action. Applications must not use these events as an authorization boundary without their own independent security mechanism. - -## 19. Modern keyboard, focus, mouse, and paste privacy - -Modern keyboard protocols can expose press/repeat/release phase, associated text, shifted/base-layout identities, and modifier state. Focus/mouse reports expose interaction context. Bracketed-paste data may contain arbitrary user text. - -Applications should collect, log, and transmit only what they need. Bracketed paste marks provenance and boundaries; it does not make pasted content safe to execute. - -## 20. Terminal observations can fingerprint the environment - -Explicit queries and capability verification can reveal terminal/environment characteristics such as device attributes, supported protocol families, graphics support, cursor/color state, clipboard state, or notification support. - -Applications should issue only observations they need. - -`InspectCapability(...)` performs no terminal I/O. `VerifyCapabilityAsync(...)` is explicit so the application controls whether the benefit of stronger capability evidence justifies the terminal traffic and possible fingerprinting signal. - -`DisplayRasterAsync(...)` may use the reviewed raster capability/probe machinery when necessary; it does not conduct broad emulator-brand fingerprinting. `CreateRasterResourceAsync(...)` requires already-verified persistent capability and does not perform a new hidden fingerprinting query as a side effect of creation. - -## 21. Redirected endpoints - -Semantic operations that require a live terminal reject known redirected/non-terminal output rather than blindly writing control bytes into a file or pipe. - -Active queries additionally require compatible interactive input/output endpoints through the shared query contract. - -Capability support knowledge is not rewritten merely because the current endpoint is unavailable. Endpoint availability is represented separately from support truth. - -Persistent resource creation and mutation follow the same endpoint discipline; registry allocation does not justify emitting control traffic to an unsuitable endpoint. - -## 22. Restoration, lifecycle, and evidence invalidation - -When `Icod.Terminal` claims exact restoration, it establishes a truthful baseline first. Unknown state is not replaced by a guessed default while being described as restoration. - -Suspend/resume and explicit invalidation are trust boundaries for live observations. Generation-scoped live probe/protocol-response evidence expires through the semantic evidence generation mechanism. Immutable selected description/profile evidence may persist because it describes static session configuration rather than a prior live observation. - -Already-returned `TerminalCapabilityStatus` values are immutable snapshots. They do not update themselves across lifecycle changes; callers inspect again when current knowledge matters. - -Ephemeral raster display and notification observations are not automatically replayed after resume and are not represented as exactly restorable terminal state. - -Persistent raster handles are also not replayable restoration state. Their terminal-side identities are valid only for the lifecycle generation that established them. Invalidation/resume makes existing handles stale; the library does not retain hidden raster copies, silently re-upload them, or emit stale ids during later disposal. - -## 23. Dependencies and native boundaries - -Native platform APIs are used only for terminal-control/lifecycle operations that require them. The package does not hide PTY process hosting, shell execution, browser/network access, OS clipboard integration, image decoding, or native desktop notification APIs behind terminal semantic methods. - -`Icod.Terminal.csproj` remains the package authority for its direct NuGet dependencies. Tests, samples, and tools do not need to duplicate exact transitive dependency versions as a security or compatibility mechanism; successful restore/build of the declared package graph is the dependency witness. - -Sixel, Kitty Graphics, notification protocols, and capability probes are terminal traffic only. - -## 24. Reporting security issues +```text +Icod.TermInfo 1.11.0 +Icod.Timing 1.0.0 +``` -Security defects should be reported through the repository owner's supported private security-reporting channel when available rather than publishing exploitable details before a fix can be prepared. +Additional test/sample consumers used for qualification do not widen the package's runtime trust/dependency boundary. -Compatibility or missing-feature requests should remain distinct from security reports. +## 18. Stable exclusions after 1.12 -## 25. Permanent security principles +Security/privacy behavior does not include promises for: -For stable 1.x, new features should preserve these principles: +- terminal authenticity; +- cryptographic integrity/confidentiality of terminal protocol traffic; +- scene-graph ordering across independent applications; +- relative placement graphs or caller-manufactured protocol identities; +- Unicode placeholder virtual placement; +- animation/frame lifecycle; +- automatic persistent-raster replay/re-upload; +- Sixel persistent emulation; +- hidden image caches; +- image-file decoding; +- PTY/ConPTY process hosting. -1. expose semantic intent rather than generic dangerous protocol dispatch; -2. validate and bound untrusted payloads before commitment where possible; -3. keep parsing, conversion, event buffering, capability verification, persistent ownership registries, and resynchronization bounded; -4. preserve one authoritative input/query/event reader; -5. do not infer support solely from brand/environment identity; -6. separate capability support, endpoint availability, and evidence lifetime; -7. keep ordinary inspection side-effect free and make terminal probing explicit; -8. treat correlation or semantic recognition as bounded ownership rather than trust; -9. distinguish emission from terminal application or acknowledgement, and acknowledgement from authentication; -10. treat unsolicited semantic events, query responses, and graphics acknowledgements as unauthenticated external input; -11. make metadata disclosure explicit; -12. do not claim exact restoration without a truthful baseline; -13. surface uncertainty and compound failures rather than hiding them; -14. avoid hidden host execution, network access, file decoding, or process-global side effects; -15. once a terminal graphics transaction is committed, preserve logical-transfer integrity rather than using ordinary caller cancellation to truncate it; -16. never automatically replay or switch backends after partial committed graphics failure; -17. do not turn typed semantic event or capability-planning support into a generic raw vendor/protocol bus; -18. do not expose dependency/backend provenance as authentication or terminal identity; -19. keep persistent protocol identities private behind opaque session-owned handles; -20. treat lifecycle invalidation and terminal `ENOENT` as loss of persistent-state certainty rather than silently replaying or inventing state; -21. do not retain arbitrary raster source data solely to provide hidden persistent-state replay. +Source rectangles and signed z-order are bounded placement inputs only; they do not weaken these exclusions. diff --git a/docs/releases/1.12.0.md b/docs/releases/1.12.0.md new file mode 100644 index 000000000..bf68da5a0 --- /dev/null +++ b/docs/releases/1.12.0.md @@ -0,0 +1,141 @@ +# Icod.Terminal 1.12.0 + +`Icod.Terminal 1.12.0` extends the opaque persistent-raster placement model with bounded source-pixel cropping and signed z-order while preserving the 1.11 ownership, acknowledgement, lifecycle, cleanup, and dependency contracts. + +The package targets: + +```text +net8.0 +net9.0 +net10.0 +``` + +The stable compatibility floor remains `1.0.0`. + +## Advanced persistent-raster placement + +Version 1.12 adds the immutable backend-neutral source rectangle: + +```csharp +TerminalRasterSourceRectangle source = new( + 0, + 0, + 320, + 180 +); +``` + +Persistent placement options now support: + +```csharp +TerminalRasterPlacementOptions options = new() { + SourceRectangle = source, + Columns = 40, + ZIndex = -1 +}; +``` + +`SourceRectangle` coordinates are zero-based source-image pixels. Width and height must be positive, scalar values remain bounded by the raster dimension ceiling, and create/update validation requires the complete rectangle to fit inside the resource before any placement output is committed. + +`ZIndex` is nullable signed `int` and accepts the full `int.MinValue..int.MaxValue` range. `null` retains backend/default order. + +Create and update use the same acknowledged placement transaction. A source rectangle, when present, is emitted atomically as all four crop fields; z-order uses invariant signed decimal formatting. Existing placement bytes and behavior remain unchanged when the new options are omitted. + +## Ownership remains opaque and bounded + +Version 1.12 does not expose terminal image ids, placement ids, raw APC dictionaries, or backend selection. Placement position remains the current terminal cursor location; source cropping is not absolute screen positioning. + +The existing bounds remain unchanged: + +```text +maximum live persistent resources 256 per session +maximum live persistent placements 4096 per session +``` + +Persistent identities remain generation scoped. Invalidation makes existing handles stale; stale mutation returns controlled `Unavailable` before output and stale disposal remains local-only. The library does not retain source-image copies for hidden replay. + +## Acknowledgement and adversarial behavior + +Advanced geometry uses the same authoritative input/query path as 1.11 persistent placement operations. + +Qualification covers: + +- wrong image and placement identities; +- malformed and duplicate correlated fields; +- bounded timeout and late-response ownership; +- correlated `ENOENT` invalidation; +- source rectangles ending exactly at source right/bottom edges; +- invalid create/update rectangles producing no new output; +- `int.MinValue` and `int.MaxValue` z-order through real acknowledged operations; +- generation invalidation and stale local-only cleanup; +- repeated create/place/update/delete cycles with advanced geometry. + +Correlation establishes transaction ownership, not terminal authenticity. + +## TermInfo evidence cleanup + +Before adding the public geometry surface, 1.12 table-drives the internal `TerminalTermInfoSemanticEvidence` exact-semantic and metadata-backed backend rules. This is behavior-preserving internal cleanup: evidence states, routing outcomes, public API, and package dependencies remain unchanged. + +## Samples and package consumption + +`Icod.Terminal.PersistentRaster.Sample` now demonstrates a bounded source-pixel crop and nonzero z-order, then updates the same placement with a different crop and stacking intent. The example remains backend-neutral and exposes no Kitty/Sixel ids or raw commands. + +The fresh NuGet-only persistent-raster smoke consumer compiles and executes `TerminalRasterSourceRectangle`, `SourceRectangle`, and `ZIndex` on `net8.0`, `net9.0`, and `net10.0`. Package verification also requires generated XML documentation for the rectangle type, constructor, four properties, and both new placement-option properties on every supported TFM. + +Current `Icod.DCurses` package acceptance/hardening remains a required downstream witness; no downstream code change is expected for this additive release. + +## Public API + +The only intended public additions over 1.11 are: + +```text +TerminalRasterSourceRectangle +TerminalRasterSourceRectangle..ctor(int,int,int,int) +TerminalRasterSourceRectangle.X +TerminalRasterSourceRectangle.Y +TerminalRasterSourceRectangle.Width +TerminalRasterSourceRectangle.Height +TerminalRasterPlacementOptions.SourceRectangle +TerminalRasterPlacementOptions.ZIndex +``` + +The final 1.12 public API fingerprint is: + +```text +eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8 +``` + +Historical public API baselines remain unchanged. + +## Dependencies + +The production dependency graph remains: + +```text +Icod.TermInfo 1.11.0 +Icod.Timing 1.0.0 +``` + +No Inspection, Source, graphics-codec, or scene-layout dependency is added to the production package. + +## Deliberate exclusions + +Version 1.12 does not add: + +- relative placement graphs or parent placement identities; +- absolute screen-coordinate or pixel-offset placement; +- Unicode placeholder/virtual placements; +- animation or frame lifecycle; +- caller-selected raster backends or raw Kitty command dispatch; +- automatic replay/re-upload after invalidation; +- hidden source-image caches; +- Sixel persistence emulation; +- image-file decoding/transcoding; +- PTY/ConPTY hosting; +- cells, windows, damage, or layout policy owned by `Icod.DCurses`. + +## Compatibility + +Version 1.12 is an additive minor release over the stable `1.0.0` compatibility floor. Existing signatures and enum numeric values are preserved. Existing 1.11 persistent placement behavior is byte-for-byte preserved when `SourceRectangle` and `ZIndex` are not supplied. + +See [`Persistent-Raster-Ownership.md`](../Persistent-Raster-Ownership.md), [`Compatibility-and-Versioning.md`](../Compatibility-and-Versioning.md), [`Public-API-Baseline-1.12.md`](../Public-API-Baseline-1.12.md), and [`Icod.Terminal-1.12.0-Development-Roadmap.md`](../../Icod.Terminal-1.12.0-Development-Roadmap.md) for the permanent contract and development evidence. From ef02714bcbd9ab84572a7ed8200a0ad8071d831a Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 16:50:12 -0400 Subject: [PATCH 50/63] docs: record Icod.Terminal 1.12.0 release candidate closure --- Icod.Terminal-1.12.0-Development-Roadmap.md | 76 +++++---- docs/T127-1.12.0-Release-Closure.md | 167 ++++++++++++++++++++ 2 files changed, 215 insertions(+), 28 deletions(-) create mode 100644 docs/T127-1.12.0-Release-Closure.md diff --git a/Icod.Terminal-1.12.0-Development-Roadmap.md b/Icod.Terminal-1.12.0-Development-Roadmap.md index ff6c42eb0..441b96837 100644 --- a/Icod.Terminal-1.12.0-Development-Roadmap.md +++ b/Icod.Terminal-1.12.0-Development-Roadmap.md @@ -2,7 +2,7 @@ **Release:** `1.12.0` **Theme:** bounded advanced persistent-raster placement geometry -**Status:** T120–T126 accepted; T127 stable release-candidate qualification pending +**Status:** T120–T126 accepted; T127 stable release candidate accepted; closure-only exact-head qualification is the final gate **Stable compatibility floor:** `1.0.0` **Prior release:** published `1.11.1` @@ -76,7 +76,7 @@ T123 z-order public contract + validation accep T124 create/update encoder and acknowledged placement integration accepted T125 lifecycle/adversarial/boundary hardening accepted T126 sample/package-only consumer/XML docs/downstream qualification accepted -T127 API freeze/release docs/three-OS/package release closure in progress +T127 API freeze/release docs/three-OS/package release closure release candidate accepted; final closure matrix pending ``` ## Accepted checkpoints @@ -162,37 +162,58 @@ Acceptance includes: - generated XML documentation checks for the new type, constructor, four properties, and both new placement-option members on all package TFMs; - current Stable 1.x downstream `Icod.DCurses` acceptance/hardening soak with no downstream code change. -## T127 — stable release closure +### T127 — stable release candidate + +Accepted release candidate exact head: -The stable release candidate must: +```text +0b7961f6d8151253be57f65a69e17a12ec4bdec5 +``` -1. keep the public API fingerprint exactly `eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8`; -2. set package version metadata to stable `1.12.0`; -3. synchronize README, changelog, release notes, current roadmap, architecture, persistent ownership, security/privacy, and compatibility authorities; -4. preserve production dependencies exactly: +Workflow: ```text -Icod.TermInfo 1.11.0 -Icod.Timing 1.0.0 +#1659 / 34717812704 +``` + +All nine PR jobs passed, including stable `1.12.0` package metadata, final API freeze, current downstream soak, and validated package artifact. + +Validated package artifact: + +```text +id: 10305651607 +digest: sha256:ed5faa549819e89b35e03dc06a5e1061371a15fbd4336a46fbe25d6631408bbe ``` -5. pass the exact-head full Staging matrix: +Package candidate artifact: ```text -Runtime Windows -Runtime Linux -Runtime macOS -Package candidate / public API freeze -Package Foundation -Package Presentation -Package Semantic and hardening -Package Stable 1.x release line -Validated package artifact +id: 10305430874 +digest: sha256:700dac08d2a7655d77e113b5051391a05a88ef7cefc1c2c542841534adab37be ``` -6. record the accepted candidate SHA/workflow/fingerprint/dependency/downstream evidence in a final closure document; -7. run the same full matrix once more after closure-only status documentation; -8. leave merge, mainline Release validation, `v1.12.0` tagging, GitHub Release creation, and NuGet publication to the maintainer/release workflow. +The final T127 evidence authority is [`docs/T127-1.12.0-Release-Closure.md`](docs/T127-1.12.0-Release-Closure.md). + +## T127 — stable release closure + +The accepted stable release candidate: + +1. keeps the public API fingerprint exactly `eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8`; +2. sets package version metadata to stable `1.12.0`; +3. synchronizes README, changelog, release notes, current roadmap, architecture, persistent ownership, security/privacy, and compatibility authorities; +4. preserves production dependencies exactly: + +```text +Icod.TermInfo 1.11.0 +Icod.Timing 1.0.0 +``` + +5. passed the exact-head full Staging matrix on `0b7961f6d8151253be57f65a69e17a12ec4bdec5` in workflow `#1659 / 34717812704`; +6. records the accepted candidate SHA/workflow/fingerprint/dependency/downstream evidence in `docs/T127-1.12.0-Release-Closure.md`; +7. requires the same full matrix once more on the closure-only status-documentation head; +8. leaves merge, mainline Release validation, `v1.12.0` tagging, GitHub Release creation, and NuGet publication to the maintainer/release workflow. + +The final closure-only head changes documentation status only; production code, package metadata, dependencies, and the public API remain identical to the accepted release candidate. ## Compatibility guardrails @@ -236,9 +257,8 @@ T120 accepted -> T124 accepted -> T125 accepted -> T126 accepted - -> T127 stable candidate - -> exact-head matrix - -> closure-only record - -> final exact-head matrix - -> maintainer handoff + -> T127 stable candidate accepted (#1659) + -> closure-only record + -> final exact-head matrix + -> maintainer handoff ``` diff --git a/docs/T127-1.12.0-Release-Closure.md b/docs/T127-1.12.0-Release-Closure.md new file mode 100644 index 000000000..df6362bfa --- /dev/null +++ b/docs/T127-1.12.0-Release-Closure.md @@ -0,0 +1,167 @@ +# T127 — Icod.Terminal 1.12.0 Release Closure + +**Release:** `1.12.0` +**Theme:** bounded advanced persistent-raster placement geometry +**Stable compatibility floor:** `1.0.0` +**Prior release:** published `1.11.1` + +## Purpose + +This document records the final T127 release-candidate evidence for `Icod.Terminal 1.12.0` and defines the last exact-head qualification step before maintainer handoff. + +T127 does not widen the feature scope. The final public additions remain exactly the source-rectangle value contract plus nullable signed z-order on `TerminalRasterPlacementOptions`. + +## Accepted release candidate + +Stable release metadata and synchronized release-facing authorities were qualified on exact head: + +```text +0b7961f6d8151253be57f65a69e17a12ec4bdec5 +``` + +Pull-request workflow: + +```text +#1659 / 34717812704 +``` + +The workflow completed successfully on all nine required jobs: + +```text +Runtime Windows +Runtime Linux +Runtime macOS +Package candidate / public API freeze +Package Foundation +Package Presentation +Package Semantic and hardening +Package Stable 1.x release line +Validated package artifact +``` + +The validated package artifact was: + +```text +artifact id: 10305651607 +name: icod-terminal-pr-packages +digest: sha256:ed5faa549819e89b35e03dc06a5e1061371a15fbd4336a46fbe25d6631408bbe +``` + +The package-candidate artifact was: + +```text +artifact id: 10305430874 +name: icod-terminal-pr-package-candidate +digest: sha256:700dac08d2a7655d77e113b5051391a05a88ef7cefc1c2c542841534adab37be +``` + +## Final public API freeze + +The final deterministic public API fingerprint remains: + +```text +eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8 +``` + +It is identical across `net8.0`, `net9.0`, and `net10.0` and is recorded by: + +```text +docs/Public-API-Baseline-1.12.md +docs/Public-API-Baseline-1.12.sha256 +``` + +The only additive 1.12 public members are: + +```text +TerminalRasterSourceRectangle +TerminalRasterSourceRectangle..ctor(int,int,int,int) +TerminalRasterSourceRectangle.X +TerminalRasterSourceRectangle.Y +TerminalRasterSourceRectangle.Width +TerminalRasterSourceRectangle.Height +TerminalRasterPlacementOptions.SourceRectangle +TerminalRasterPlacementOptions.ZIndex +``` + +No public API was added after T123. + +## Dependency and package boundary + +The production dependency graph remains exactly: + +```text +Icod.TermInfo 1.11.0 +Icod.Timing 1.0.0 +``` + +`Icod.TermInfo.Inspection` remains integration-test/sample-only and `Icod.TermInfo.Source` is not introduced into the production package graph. + +Stable package metadata is `1.12.0` with no prerelease suffix. + +## Accepted tranche checkpoints + +```text +T120 architecture/API regret gate accepted +T121 table-driven TermInfo semantic evidence accepted +T122 source rectangle + resource-aware validation accepted +T123 signed z-order + final API freeze accepted +T124 placement encoder/acknowledged transaction aba1f7c0989d2c451294edf75590637c227a7e05 #1653 / 34713178166 +T125 adversarial/lifecycle/boundary hardening 799d096fa439c43b4f31399a551c4524fe40fa10 #1657 / 34716833678 +T126 sample/package/XML/downstream qualification 7b38994c1ba936df5887d1aa015394c4ed626ddf #1658 / 34717103814 +T127 stable release candidate 0b7961f6d8151253be57f65a69e17a12ec4bdec5 #1659 / 34717812704 +``` + +Every listed workflow from T124 onward passed the full nine-job PR matrix. + +## Frozen 1.12 behavior + +Version 1.12 adds two narrowly bounded placement controls: + +- `SourceRectangle` selects a source-pixel crop which must fit completely inside the uploaded resource; +- `ZIndex` accepts the complete signed `int` range and expresses relative stacking intent. + +The release preserves: + +- current-cursor placement and no-cursor-movement graphics semantics; +- byte-compatible 1.11 placement output when the new options are omitted; +- opaque private terminal image/placement identities; +- one authoritative input/query path; +- acknowledgement correlation and bounded parsing; +- generation-scoped resource/placement certainty; +- local-only stale cleanup; +- no hidden source-raster cache or automatic replay/re-upload; +- 256-resource and 4096-placement local ceilings; +- direct persistent transfer only. + +The release deliberately excludes relative placement graphs, Unicode placeholders, animation/frame lifecycle, absolute screen-coordinate placement, caller-selected backends, public Kitty identities, generic raw Kitty dispatch, image decoding/transcoding, PTY/ConPTY hosting, and cells/windows/layout policy. + +## Consumer and downstream qualification + +The accepted package qualification proves: + +- the backend-neutral persistent-raster sample creates and updates source crops and z-order; +- fresh NuGet-only consumers compile/run `TerminalRasterSourceRectangle`, `SourceRectangle`, and `ZIndex` on all three supported TFMs; +- generated XML documentation contains the new type, constructor, rectangle properties, and new placement-option properties on all three package TFMs; +- current Stable 1.x `Icod.DCurses` acceptance/hardening soak remains green without a downstream code change. + +## Final status-only qualification + +This closure record and the accompanying roadmap status changes are documentation-only. They do not change production code, package dependencies, package version, public API, or runtime behavior. + +Before T127 is considered closed, the exact head containing this record must pass the same complete nine-job pull-request matrix one final time. + +Only after that exact-head green result may PR #55 be marked ready for review. + +## Maintainer handoff + +After final exact-head PR qualification, the remaining release actions are intentionally left to the maintainer/release workflow: + +```text +merge PR #55 to main + -> successful main Release validation + -> tag v1.12.0 + -> create GitHub Release as appropriate + -> publish Icod.Terminal 1.12.0 to NuGet +``` + +No merge, tag, GitHub Release, or NuGet publication is performed by this closure record. From 6c2290d29cc328d777949eb52a93d6a5cf1ad82c Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:28:24 -0400 Subject: [PATCH 51/63] test: expose default raster source rectangle bypass --- ...rminalRasterDefaultSourceRectangleTests.cs | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterDefaultSourceRectangleTests.cs diff --git a/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterDefaultSourceRectangleTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterDefaultSourceRectangleTests.cs new file mode 100644 index 000000000..1b4965467 --- /dev/null +++ b/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterDefaultSourceRectangleTests.cs @@ -0,0 +1,235 @@ +/* + Icod.Terminal.Tests + Automated test suite for the Icod.Terminal library. + Copyright (C) 2026 Timothy J. Bruce +*/ + +/* + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +namespace Icod.Terminal.Tests.Graphics; + +using System.Text; +using System.Threading.Channels; +using Icod.Terminal; +using Icod.TermInfo; +using Xunit; + +/// +/// Verifies that the default source-rectangle value cannot bypass intrinsic geometry validation. +/// +public sealed class TerminalRasterDefaultSourceRectangleTests { + [Fact] + public void ResourceAwareValidationRejectsDefaultRectangle() { + TerminalRasterPlacementOptions options = new() { + SourceRectangle = default( TerminalRasterSourceRectangle ) + }; + + _ = Assert.Throws( + () => options.Validate( + 4, + 3 + ) + ); + } + + [Fact] + public void EncoderRejectsDefaultRectangle() { + TerminalRasterPlacementOptions options = new() { + SourceRectangle = default( TerminalRasterSourceRectangle ) + }; + + _ = Assert.Throws( + () => KittyGraphicsPersistentEncoder.EncodePlacementPayload( + imageId: 99, + placementId: 7, + options + ) + ); + } + + [Fact] + public async Task PublicCreateAndUpdateRejectDefaultRectangleBeforeOutput() { + ScriptedTransport transport = new(); + await using TerminalSession session = await OpenSessionAsync( transport ); + TerminalRasterImage image = TerminalRasterImage.CreateRgb24( + 2, + 2, + new byte[ 2 * 2 * 3 ] + ); + + Task> resourceCreation = + session.CreateRasterResourceAsync( image ).AsTask(); + await transport.WaitForWriteCountAsync( 1 ); + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=77,I=1;OK\u001b\\" ) + ); + TerminalRasterResource resource = Assert.IsType( + ( await resourceCreation ).Value + ); + await using ( resource ) { + int beforeInvalidCreate = transport.Writes.Count; + await Assert.ThrowsAsync( + async () => await resource.CreatePlacementAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = default( TerminalRasterSourceRectangle ) + } + ) + ); + Assert.Equal( beforeInvalidCreate, transport.Writes.Count ); + + Task> placementCreation = + resource.CreatePlacementAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = new TerminalRasterSourceRectangle( + 0, + 0, + 1, + 1 + ) + } + ).AsTask(); + await transport.WaitForWriteCountAsync( 2 ); + transport.Publish( + Encoding.ASCII.GetBytes( "\u001b_Gi=77,p=1;OK\u001b\\" ) + ); + TerminalRasterPlacement placement = Assert.IsType( + ( await placementCreation ).Value + ); + await using ( placement ) { + int beforeInvalidUpdate = transport.Writes.Count; + await Assert.ThrowsAsync( + async () => await placement.UpdateAsync( + new TerminalRasterPlacementOptions { + SourceRectangle = default( TerminalRasterSourceRectangle ) + } + ) + ); + Assert.Equal( beforeInvalidUpdate, transport.Writes.Count ); + } + } + } + + private static async ValueTask OpenSessionAsync( + ScriptedTransport transport + ) { + ArgumentNullException.ThrowIfNull( transport ); + TerminalSession session = await TerminalSession.OpenAsync( + new RecordingTerminalControlProvider(), + TerminalEndpoint.StandardInput, + TerminalEndpoint.StandardOutput, + transport, + transport, + new TerminalSessionOptions { + TerminalOverride = TerminalProfiles.Dumb, + ConfigureOutput = false, + MonotonicClock = new FrozenMonotonicClock(), + ObserveLifecycleEvents = false, + RequireInteractiveOutput = false + } + ); + session.RecordSemanticBackendEvidence( + TerminalProtocolBackend.ApcKittyGraphics, + TerminalCapabilitySupportState.Verified, + TerminalCapabilityEvidenceSource.ProtocolResponse + ); + return session; + } + + private sealed class ScriptedTransport : ITerminalInput, ITerminalOutput { + private readonly Channel input = Channel.CreateUnbounded( + new UnboundedChannelOptions { + SingleReader = true, + SingleWriter = false, + AllowSynchronousContinuations = false + } + ); + private readonly object synchronization = new(); + private readonly SemaphoreSlim writeSignal = new( 0 ); + private readonly List writes = []; + + internal IReadOnlyList Writes { + get { + lock ( this.synchronization ) { + return this.writes.Select( + static value => value.ToArray() + ).ToArray(); + } + } + } + + public async ValueTask ReadAsync( + Memory buffer, + CancellationToken cancellationToken = default + ) { + byte[] value = await this.input.Reader.ReadAsync( + cancellationToken + ).ConfigureAwait( false ); + if ( value.Length > buffer.Length ) { + throw new InvalidOperationException( + "The scripted response exceeds the terminal input buffer." + ); + } + + value.AsSpan().CopyTo( buffer.Span ); + return value.Length; + } + + public ValueTask WriteAsync( + ReadOnlyMemory buffer, + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + lock ( this.synchronization ) { + this.writes.Add( buffer.ToArray() ); + } + this.writeSignal.Release(); + return ValueTask.CompletedTask; + } + + public ValueTask FlushAsync( + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + return ValueTask.CompletedTask; + } + + internal void Publish( + byte[] value + ) { + ArgumentNullException.ThrowIfNull( value ); + if ( !this.input.Writer.TryWrite( value.ToArray() ) ) { + throw new InvalidOperationException( + "The scripted terminal input channel rejected a response." + ); + } + } + + internal async Task WaitForWriteCountAsync( + int count + ) { + if ( 0 > count ) { + throw new ArgumentOutOfRangeException( nameof( count ) ); + } + + using CancellationTokenSource timeout = new(); + timeout.CancelAfter( TimeSpan.FromSeconds( 5 ) ); + while ( this.Writes.Count < count ) { + await this.writeSignal.WaitAsync( + timeout.Token + ).ConfigureAwait( false ); + } + } + } +} From f5f0a4f69d3571083f63654acd35b9893e8807dd Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:32:19 -0400 Subject: [PATCH 52/63] test: make default rectangle red witness deterministic --- ...rminalRasterDefaultSourceRectangleTests.cs | 78 +++++++++++++++++-- 1 file changed, 70 insertions(+), 8 deletions(-) diff --git a/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterDefaultSourceRectangleTests.cs b/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterDefaultSourceRectangleTests.cs index 1b4965467..6db9a0574 100644 --- a/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterDefaultSourceRectangleTests.cs +++ b/tests/Icod.Terminal.Tests/src/Graphics/TerminalRasterDefaultSourceRectangleTests.cs @@ -20,6 +20,7 @@ You should have received a copy of the GNU General Public License */ namespace Icod.Terminal.Tests.Graphics; +using System.Globalization; using System.Text; using System.Threading.Channels; using Icod.Terminal; @@ -89,8 +90,8 @@ await Assert.ThrowsAsync( ); Assert.Equal( beforeInvalidCreate, transport.Writes.Count ); - Task> placementCreation = - resource.CreatePlacementAsync( + TerminalControlResult placementCreation = + await resource.CreatePlacementAsync( new TerminalRasterPlacementOptions { SourceRectangle = new TerminalRasterSourceRectangle( 0, @@ -99,13 +100,9 @@ await Assert.ThrowsAsync( 1 ) } - ).AsTask(); - await transport.WaitForWriteCountAsync( 2 ); - transport.Publish( - Encoding.ASCII.GetBytes( "\u001b_Gi=77,p=1;OK\u001b\\" ) - ); + ); TerminalRasterPlacement placement = Assert.IsType( - ( await placementCreation ).Value + placementCreation.Value ); await using ( placement ) { int beforeInvalidUpdate = transport.Writes.Count; @@ -195,6 +192,7 @@ public ValueTask WriteAsync( this.writes.Add( buffer.ToArray() ); } this.writeSignal.Release(); + this.PublishPlacementAcknowledgement( buffer.Span ); return ValueTask.CompletedTask; } @@ -231,5 +229,69 @@ await this.writeSignal.WaitAsync( ).ConfigureAwait( false ); } } + + private void PublishPlacementAcknowledgement( + ReadOnlySpan frame + ) { + string text = Encoding.ASCII.GetString( frame ); + if ( !text.StartsWith( + "\u001b_Ga=p,", + StringComparison.Ordinal + ) || !TryReadIdentityField( + text, + ",i=", + out uint imageId + ) || !TryReadIdentityField( + text, + ",p=", + out uint placementId + ) ) { + return; + } + + this.Publish( + Encoding.ASCII.GetBytes( + string.Create( + CultureInfo.InvariantCulture, + $"\u001b_Gi={imageId},p={placementId};OK\u001b\\" + ) + ) + ); + } + + private static bool TryReadIdentityField( + string text, + string marker, + out uint value + ) { + ArgumentNullException.ThrowIfNull( text ); + ArgumentException.ThrowIfNullOrEmpty( marker ); + value = 0u; + + int start = text.IndexOf( + marker, + StringComparison.Ordinal + ); + if ( 0 > start ) { + return false; + } + start += marker.Length; + int end = start; + while ( end < text.Length + && text[ end ] is >= '0' and <= '9' ) { + ++end; + } + + return start < end + && uint.TryParse( + text.AsSpan( + start, + end - start + ), + CultureInfo.InvariantCulture, + out value + ) + ; + } } } From 4d509c75decc37d280a92769fe668d3c6fbd41ce Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:35:54 -0400 Subject: [PATCH 53/63] fix: reject default raster source rectangles --- .../Program.cs | 4 +- samples/README.md | 4 +- .../TerminalRasterPlacementOptions.cs | 3 + src/Graphics/TerminalRasterSourceRectangle.cs | 55 +++++++++++++++---- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/samples/Icod.Terminal.PersistentRaster.Sample/Program.cs b/samples/Icod.Terminal.PersistentRaster.Sample/Program.cs index fc8fa0d27..d218d32c6 100644 --- a/samples/Icod.Terminal.PersistentRaster.Sample/Program.cs +++ b/samples/Icod.Terminal.PersistentRaster.Sample/Program.cs @@ -111,7 +111,7 @@ await session.WriteTextAsync( await using TerminalRasterPlacement placement = placementResult.Value; await session.WriteTextAsync( - "\r\nThe placement uses a source-pixel crop and relative z-order while the terminal-resident resource remains owned.\r\n" + "\r\nThe placement uses a source-pixel crop and signed z-order while the terminal-resident resource remains owned.\r\n" ); TerminalControlMutationResult update = await placement.UpdateAsync( new TerminalRasterPlacementOptions { @@ -137,7 +137,7 @@ await session.WriteTextAsync( } await session.WriteTextAsync( - "\r\nThe crop and relative stacking intent were updated; disposal will release placement and resource ownership.\r\n" + "\r\nThe crop and stacking order were updated; disposal will release placement and resource ownership.\r\n" ); return 0; diff --git a/samples/README.md b/samples/README.md index 723d151ca..0024c4e19 100644 --- a/samples/README.md +++ b/samples/README.md @@ -91,7 +91,7 @@ The normal evidence-driven router may use verified Kitty Graphics or verified Si ### `Icod.Terminal.PersistentRaster.Sample` -Demonstrates the persistent-raster ownership model using semantic APIs only, including 1.12 source-pixel cropping and relative z-order. +Demonstrates the persistent-raster ownership model using semantic APIs only, including 1.12 source-pixel cropping and signed z-order. ```text dotnet run --project samples/Icod.Terminal.PersistentRaster.Sample/Icod.Terminal.PersistentRaster.Sample.csproj -f net10.0 @@ -106,7 +106,7 @@ The sample: 5. updates the same placement at the current cursor with a different crop, extent, and z-order; 6. uses `await using` so placement/resource cleanup is deterministic. -`TerminalRasterSourceRectangle` coordinates are measured in source pixels and select which part of the owned raster resource participates in one placement. `ZIndex` expresses relative stacking intent. Neither option turns `Icod.Terminal` into a scene-layout engine: 1.12 still does not own relative placement graphs, screen-coordinate layout, or automatic composition policy. +`TerminalRasterSourceRectangle` coordinates are measured in source pixels and select which part of the owned raster resource participates in one placement. `ZIndex` expresses signed stacking order. Neither option turns `Icod.Terminal` into a scene-layout engine: 1.12 still does not own relative placement graphs, screen-coordinate layout, or automatic composition policy. The sample does not mention Kitty, Sixel, image ids, image numbers, placement ids, or terminal brand. It also does not imply that resources are replayed after lifecycle invalidation. diff --git a/src/Graphics/TerminalRasterPlacementOptions.cs b/src/Graphics/TerminalRasterPlacementOptions.cs index c806f1bf7..663cf2141 100644 --- a/src/Graphics/TerminalRasterPlacementOptions.cs +++ b/src/Graphics/TerminalRasterPlacementOptions.cs @@ -69,6 +69,9 @@ internal void Validate() { this.Rows, nameof( this.Rows ) ); + if ( this.SourceRectangle is TerminalRasterSourceRectangle rectangle ) { + rectangle.Validate(); + } } internal void Validate( diff --git a/src/Graphics/TerminalRasterSourceRectangle.cs b/src/Graphics/TerminalRasterSourceRectangle.cs index c45b22b10..d990a6206 100644 --- a/src/Graphics/TerminalRasterSourceRectangle.cs +++ b/src/Graphics/TerminalRasterSourceRectangle.cs @@ -37,20 +37,14 @@ public TerminalRasterSourceRectangle( int width, int height ) { - ValidateCoordinate( + Validate( x, - nameof( x ) - ); - ValidateCoordinate( y, - nameof( y ) - ); - ValidateExtent( width, - nameof( width ) - ); - ValidateExtent( height, + nameof( x ), + nameof( y ), + nameof( width ), nameof( height ) ); @@ -88,6 +82,47 @@ public int Height { get; } + internal void Validate() { + Validate( + this.X, + this.Y, + this.Width, + this.Height, + nameof( this.X ), + nameof( this.Y ), + nameof( this.Width ), + nameof( this.Height ) + ); + } + + private static void Validate( + int x, + int y, + int width, + int height, + string xParameterName, + string yParameterName, + string widthParameterName, + string heightParameterName + ) { + ValidateCoordinate( + x, + xParameterName + ); + ValidateCoordinate( + y, + yParameterName + ); + ValidateExtent( + width, + widthParameterName + ); + ValidateExtent( + height, + heightParameterName + ); + } + private static void ValidateCoordinate( int value, string parameterName From c8d555567a60bd6776b47aa934feff5093cf1d25 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:44:46 -0400 Subject: [PATCH 54/63] docs: clarify signed raster z-order terminology --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 590fc0aa5..7fb87d629 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ Supported public storage forms are `Rgb24`, `Rgba32`, and `Indexed8` with an RGB ### Persistent resources and placements -Version 1.11 established a separate ownership model for terminal-resident raster data; version 1.12 adds bounded source-pixel cropping and relative z-order to the existing placement options: +Version 1.11 established a separate ownership model for terminal-resident raster data; version 1.12 adds bounded source-pixel cropping and signed z-order to the existing placement options: ```csharp TerminalCapabilityStatus capability = await session.VerifyCapabilityAsync( From 4cf87ff5973f04c7d42a602586f18250c5b7c96e Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:45:12 -0400 Subject: [PATCH 55/63] docs: clarify persistent raster z-order semantics --- docs/Persistent-Raster-Ownership.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/Persistent-Raster-Ownership.md b/docs/Persistent-Raster-Ownership.md index d6ca4790e..af42463ab 100644 --- a/docs/Persistent-Raster-Ownership.md +++ b/docs/Persistent-Raster-Ownership.md @@ -117,6 +117,7 @@ Contract: - `X` and `Y` are zero-based and non-negative; - `Width` and `Height` are positive; - scalar values respect `TerminalRasterImage.MaximumDimension`; +- every present rectangle is revalidated by placement options, including `default(TerminalRasterSourceRectangle)` values that bypass the public constructor; - the right and bottom edges must not exceed the actual source resource dimensions; - `SourceRectangle == null` means the full source image; - a present rectangle is emitted as all four crop fields together rather than partially; @@ -135,7 +136,7 @@ Source cropping selects source pixels only. It does not define terminal screen p - `null` means backend/default stacking order; - wire formatting uses invariant signed decimal representation. -Z-order is relative stacking intent for one placement. It does not create parent/child placement identity, graph lifetime ownership, cycle detection, or a general scene-composition model. +Z-order expresses signed stacking order for one placement. It does not create parent/child placement identity, graph lifetime ownership, cycle detection, or a general scene-composition model. ## 7. Complete replacement semantics From c8ca3c2b5f785f5f3bfe413b31c4655c2c581ada Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:45:41 -0400 Subject: [PATCH 56/63] docs: distinguish z-order from relative placement --- docs/Architecture.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Architecture.md b/docs/Architecture.md index a0323db2b..731b0295c 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -160,7 +160,7 @@ Placement position remains the terminal's current cursor location. `Columns` and Version 1.12 adds `TerminalRasterSourceRectangle` and `TerminalRasterPlacementOptions.SourceRectangle`. -Coordinates are zero-based source-image pixels. `Width` and `Height` are positive. Scalar values remain within the raster dimension ceiling, and the complete rectangle must fit inside the owning resource before placement output commits. +Coordinates are zero-based source-image pixels. `Width` and `Height` are positive. Scalar values remain within the raster dimension ceiling, and every present rectangle is revalidated by placement options—including `default(TerminalRasterSourceRectangle)` values that bypass the public constructor—before the complete rectangle is checked against the owning resource and before placement output commits. The resource state stores immutable source width/height metadata needed for this validation. It does not retain source pixel bytes for replay. @@ -170,7 +170,7 @@ Source cropping does **not** change screen placement ownership: the placement st `TerminalRasterPlacementOptions.ZIndex` is nullable signed `int` and accepts the full CLR `int` domain. -It expresses relative stacking intent to the reviewed persistent backend. It is not a scene graph, parent/child placement chain, or global composition policy. +It expresses signed stacking order to the reviewed persistent backend. It is not a scene graph, parent/child placement chain, or global composition policy. ### 7.3 Shared placement transaction From 03a8064c674d2e872bbc12ceb4534160a8b4a9eb Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:45:59 -0400 Subject: [PATCH 57/63] docs: harden 1.12 compatibility wording --- docs/Compatibility-and-Versioning.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/Compatibility-and-Versioning.md b/docs/Compatibility-and-Versioning.md index 2ff3bee20..95d528ad0 100644 --- a/docs/Compatibility-and-Versioning.md +++ b/docs/Compatibility-and-Versioning.md @@ -83,11 +83,12 @@ A caller upgrading from 1.11 does not need to change existing placement code. When `SourceRectangle` is supplied: - it selects a source-image pixel region; +- its intrinsic scalar contract is revalidated even when the value came from `default(TerminalRasterSourceRectangle)` rather than the public constructor; - it must fit completely inside the owning resource; - invalid rectangles are rejected before new placement output; - source dimensions are retained as metadata only, not as a replay pixel cache. -When `ZIndex` is supplied, its complete signed `int` value is preserved as relative stacking intent. This does not create a general scene-layout contract. +When `ZIndex` is supplied, its complete signed `int` value is preserved as signed stacking order. This does not create a general scene-layout contract or relative-placement graph. ## 6. Query/input compatibility @@ -188,7 +189,7 @@ Validated package artifact The Stable 1.x package shard includes fresh package-only consumption and downstream acceptance/hardening where defined by the repository release contract. -After closure-only documentation changes, the same exact-head matrix is run again before the PR is marked ready for review. +Any post-closure pre-merge code or documentation change requires the same exact-head matrix again before the PR is considered merge-ready. ## 13. Maintainer release actions From df521b45e3c75da6ff55ef39f231355c3fea307b Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:46:23 -0400 Subject: [PATCH 58/63] docs: clarify raster z-order security semantics --- docs/Security-and-Privacy.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/Security-and-Privacy.md b/docs/Security-and-Privacy.md index ec5d44d11..c655c4d7a 100644 --- a/docs/Security-and-Privacy.md +++ b/docs/Security-and-Privacy.md @@ -49,7 +49,7 @@ maximum live persistent placements 4096 placement Columns / Rows 1..16384 when supplied ``` -Version 1.12 adds source rectangles without increasing source raster ceilings. Rectangle scalar values are bounded, dimensions must be positive, coordinates non-negative, and the complete rectangle must fit inside the owning resource before output. Signed z-order consumes only a bounded `int` value and adds no unbounded layer registry. +Version 1.12 adds source rectangles without increasing source raster ceilings. Rectangle scalar values are bounded, dimensions must be positive, coordinates non-negative, and every present rectangle—including `default(TerminalRasterSourceRectangle)` values that bypass the public constructor—is revalidated before the complete rectangle is checked against the owning resource and before output. Signed z-order consumes only a bounded `int` value and adds no unbounded layer registry. Relevant framing/resource ceilings remain bounded, including normal response frames, APC/DCS frames, Kitty Base64 chunk data, notification metadata, and Sixel quantization state. @@ -97,7 +97,7 @@ For unacknowledged protocols, successful completion generally means requested by For acknowledged persistent resource/placement operations, success means a well-formed correlated response was accepted under the protocol contract. It still does not authenticate the terminal or guarantee future persistence/visual stacking. -`ZIndex` is therefore a requested relative stacking intent, not a verified global scene order. +`ZIndex` is therefore a requested signed stacking order, not a verified global scene order and not a relative-placement relationship. ## 8. Raster input and source cropping @@ -141,7 +141,8 @@ While current, cleanup is child-first: placements are deleted before resource da Security-relevant guarantees: -- rectangle scalar validation is performed by constructors/options before output; +- constructor validation enforces the public rectangle scalar contract; +- placement options revalidate every present rectangle, including default struct values that bypass the constructor; - resource-aware validation prevents a crop from escaping the uploaded raster dimensions; - widened arithmetic prevents boundary arithmetic from overflowing before comparison; - a present crop is encoded as one complete reviewed four-field tuple; From cbd918aaadec88ad747a392a1204ccf827689a87 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:46:47 -0400 Subject: [PATCH 59/63] docs: record default rectangle hardening --- docs/releases/1.12.0.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/releases/1.12.0.md b/docs/releases/1.12.0.md index bf68da5a0..9a1382bf6 100644 --- a/docs/releases/1.12.0.md +++ b/docs/releases/1.12.0.md @@ -35,7 +35,7 @@ TerminalRasterPlacementOptions options = new() { }; ``` -`SourceRectangle` coordinates are zero-based source-image pixels. Width and height must be positive, scalar values remain bounded by the raster dimension ceiling, and create/update validation requires the complete rectangle to fit inside the resource before any placement output is committed. +`SourceRectangle` coordinates are zero-based source-image pixels. Width and height must be positive, scalar values remain bounded by the raster dimension ceiling, and create/update validation requires the complete rectangle to fit inside the resource before any placement output is committed. Placement options revalidate every present rectangle, including `default(TerminalRasterSourceRectangle)` values that bypass the public constructor, so a zero-sized default value cannot reach the encoder or wire. `ZIndex` is nullable signed `int` and accepts the full `int.MinValue..int.MaxValue` range. `null` retains backend/default order. @@ -65,7 +65,7 @@ Qualification covers: - bounded timeout and late-response ownership; - correlated `ENOENT` invalidation; - source rectangles ending exactly at source right/bottom edges; -- invalid create/update rectangles producing no new output; +- invalid create/update rectangles—including default struct values—producing no new output; - `int.MinValue` and `int.MaxValue` z-order through real acknowledged operations; - generation invalidation and stale local-only cleanup; - repeated create/place/update/delete cycles with advanced geometry. @@ -78,7 +78,7 @@ Before adding the public geometry surface, 1.12 table-drives the internal `Termi ## Samples and package consumption -`Icod.Terminal.PersistentRaster.Sample` now demonstrates a bounded source-pixel crop and nonzero z-order, then updates the same placement with a different crop and stacking intent. The example remains backend-neutral and exposes no Kitty/Sixel ids or raw commands. +`Icod.Terminal.PersistentRaster.Sample` now demonstrates a bounded source-pixel crop and nonzero signed z-order, then updates the same placement with a different crop and stacking order. The example remains backend-neutral and exposes no Kitty/Sixel ids or raw commands. The fresh NuGet-only persistent-raster smoke consumer compiles and executes `TerminalRasterSourceRectangle`, `SourceRectangle`, and `ZIndex` on `net8.0`, `net9.0`, and `net10.0`. Package verification also requires generated XML documentation for the rectangle type, constructor, four properties, and both new placement-option properties on every supported TFM. From 47f266e199124ecfb740b704b5b404f739ca0115 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:47:19 -0400 Subject: [PATCH 60/63] docs: record pre-merge 1.12 hardening --- Icod.Terminal-1.12.0-Development-Roadmap.md | 124 ++++++++++---------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/Icod.Terminal-1.12.0-Development-Roadmap.md b/Icod.Terminal-1.12.0-Development-Roadmap.md index 441b96837..9da6751b4 100644 --- a/Icod.Terminal-1.12.0-Development-Roadmap.md +++ b/Icod.Terminal-1.12.0-Development-Roadmap.md @@ -2,7 +2,7 @@ **Release:** `1.12.0` **Theme:** bounded advanced persistent-raster placement geometry -**Status:** T120–T126 accepted; T127 stable release candidate accepted; closure-only exact-head qualification is the final gate +**Status:** T120–T127 implemented; post-closure default-value hardening accepted; final documentation-only exact-head qualification pending **Stable compatibility floor:** `1.0.0` **Prior release:** published `1.11.1` @@ -56,11 +56,11 @@ public sealed class TerminalRasterPlacementOptions { } ``` -No public API was added after T123. +No public API was added after T123. The post-closure default-value hardening is internal-only and preserves this surface. -Source rectangles use zero-based source-image pixels and must fit completely within the owning resource. `ZIndex` accepts the full signed `int` domain. `UpdateAsync(...)` remains a complete replacement of the placement at the current cursor position, not a partial patch against prior options. +Source rectangles use zero-based source-image pixels and must fit completely within the owning resource. Every present rectangle is revalidated by placement options, including `default(TerminalRasterSourceRectangle)` values that bypass the public constructor. `ZIndex` accepts the full signed `int` domain. `UpdateAsync(...)` remains a complete replacement of the placement at the current cursor position, not a partial patch against prior options. -The final reviewed public API fingerprint is: +The final reviewed public API fingerprint remains: ```text eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8 @@ -76,18 +76,14 @@ T123 z-order public contract + validation accep T124 create/update encoder and acknowledged placement integration accepted T125 lifecycle/adversarial/boundary hardening accepted T126 sample/package-only consumer/XML docs/downstream qualification accepted -T127 API freeze/release docs/three-OS/package release closure release candidate accepted; final closure matrix pending +T127 API freeze/release docs/three-OS/package release closure accepted, then pre-merge hardening extended qualification ``` ## Accepted checkpoints -### T120–T123 - -The design/API-regret gate, behavior-preserving TermInfo evidence cleanup, source-rectangle contract/resource-aware validation, z-order contract, and final public API freeze are accepted. The only intended public additions are the eight members recorded in `docs/Public-API-Baseline-1.12.md`. - ### T124 — placement integration -Accepted on exact head: +Accepted exact head: ```text aba1f7c0989d2c451294edf75590637c227a7e05 @@ -101,7 +97,7 @@ Workflow: All nine PR jobs passed. -The accepted backend-neutral semantic contract is implemented by one shared acknowledged placement path. The reviewed wire order is: +The reviewed wire order is: ```text Ga=p,i=,p=,C=1[,x=...[,y=...[,w=...[,h=...]]]][,c=...][,r=...][,z=...] @@ -111,7 +107,7 @@ A present source rectangle emits all four crop fields together. Signed z-order u ### T125 — hardening -Accepted on exact head: +Accepted exact head: ```text 799d096fa439c43b4f31399a551c4524fe40fa10 @@ -125,22 +121,11 @@ Workflow: All nine PR jobs passed. -Acceptance covers: - -- source rectangles ending exactly at the source right/bottom edge; -- combined rectangle + Columns + Rows + signed z-order; -- invalid create/update rectangles rejected before new output; -- `int.MinValue` and `int.MaxValue` through real acknowledged placement operations; -- wrong identity, duplicate-field malformed response, correlated `ENOENT`, timeout, and late-response ownership; -- generation invalidation with controlled `Unavailable` and stale local-only disposal; -- 24 repeated advanced create/place/update/delete ownership cycles; -- unchanged 256-resource / 4096-placement ceilings and registry behavior. - -The Windows scheduler-sensitive scripted placement witness was made deterministic with the existing frozen monotonic clock; production timeout semantics were not changed. +Acceptance covers exact-edge crops, combined options, invalid-before-output behavior, `int.MinValue` / `int.MaxValue`, wrong identity, malformed/duplicate fields, correlated `ENOENT`, timeout/late-response ownership, generation invalidation, stale local-only disposal, repeated advanced ownership cycles, and unchanged capacity ceilings. ### T126 — sample/package/downstream qualification -Accepted on exact head: +Accepted exact head: ```text 7b38994c1ba936df5887d1aa015394c4ed626ddf @@ -156,15 +141,15 @@ All nine PR jobs passed. Acceptance includes: -- backend-neutral `Icod.Terminal.PersistentRaster.Sample` source cropping and z-order create/update usage; -- sample documentation explaining source-pixel crops and relative stacking intent without scene-layout claims; +- backend-neutral persistent-raster source cropping and z-order create/update usage; +- sample documentation explaining source-pixel crops and signed stacking order without scene-layout claims; - fresh NuGet-only consumption of `TerminalRasterSourceRectangle`, `SourceRectangle`, and `ZIndex` on `net8.0`, `net9.0`, and `net10.0`; -- generated XML documentation checks for the new type, constructor, four properties, and both new placement-option members on all package TFMs; +- generated XML documentation checks for the complete new public surface; - current Stable 1.x downstream `Icod.DCurses` acceptance/hardening soak with no downstream code change. -### T127 — stable release candidate +### T127 — original stable release candidate -Accepted release candidate exact head: +Accepted exact head: ```text 0b7961f6d8151253be57f65a69e17a12ec4bdec5 @@ -176,44 +161,59 @@ Workflow: #1659 / 34717812704 ``` -All nine PR jobs passed, including stable `1.12.0` package metadata, final API freeze, current downstream soak, and validated package artifact. +All nine PR jobs passed with stable `1.12.0` package metadata, the frozen public API, downstream soak, and validated package artifact. -Validated package artifact: +The first closure-only exact head was: ```text -id: 10305651607 -digest: sha256:ed5faa549819e89b35e03dc06a5e1061371a15fbd4336a46fbe25d6631408bbe +ef02714bcbd9ab84572a7ed8200a0ad8071d831a +#1660 / 34718248621 ``` -Package candidate artifact: +That head also passed all nine jobs. + +## Pre-merge default-value hardening + +A final documentation/sample/test audit identified one value-type edge case: callers can construct `default(TerminalRasterSourceRectangle)` without executing the validating public constructor. Before the fix, that zero-sized value could pass options validation and reach placement encoding. + +The deterministic RED witness was established on: ```text -id: 10305430874 -digest: sha256:700dac08d2a7655d77e113b5051391a05a88ef7cefc1c2c542841534adab37be +f5f0a4f69d3571083f63654acd35b9893e8807dd +#1662 / 34720241497 ``` -The final T127 evidence authority is [`docs/T127-1.12.0-Release-Closure.md`](docs/T127-1.12.0-Release-Closure.md). +The three new regression tests failed on every runtime TFM because no `ArgumentOutOfRangeException` was thrown: -## T127 — stable release closure +```text +ResourceAwareValidationRejectsDefaultRectangle +EncoderRejectsDefaultRectangle +PublicCreateAndUpdateRejectDefaultRectangleBeforeOutput +``` -The accepted stable release candidate: +The minimal root fix makes `TerminalRasterSourceRectangle` own one reusable intrinsic validator and makes `TerminalRasterPlacementOptions.Validate()` revalidate every present rectangle before encoding or resource-aware bounds checks. -1. keeps the public API fingerprint exactly `eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8`; -2. sets package version metadata to stable `1.12.0`; -3. synchronizes README, changelog, release notes, current roadmap, architecture, persistent ownership, security/privacy, and compatibility authorities; -4. preserves production dependencies exactly: +The GREEN implementation head was: ```text -Icod.TermInfo 1.11.0 -Icod.Timing 1.0.0 +4d509c75decc37d280a92769fe668d3c6fbd41ce +#1663 / 34720415393 ``` -5. passed the exact-head full Staging matrix on `0b7961f6d8151253be57f65a69e17a12ec4bdec5` in workflow `#1659 / 34717812704`; -6. records the accepted candidate SHA/workflow/fingerprint/dependency/downstream evidence in `docs/T127-1.12.0-Release-Closure.md`; -7. requires the same full matrix once more on the closure-only status-documentation head; -8. leaves merge, mainline Release validation, `v1.12.0` tagging, GitHub Release creation, and NuGet publication to the maintainer/release workflow. +All nine jobs passed. The public API/package freeze remained green, proving the fingerprint stayed unchanged. + +The same commit also normalized the executable sample and sample catalog from ambiguous “relative z-order” wording to **signed z-order / stacking order**, reserving “relative placement” for the explicitly excluded parent-relative placement feature. + +## Final documentation consistency pass -The final closure-only head changes documentation status only; production code, package metadata, dependencies, and the public API remain identical to the accepted release candidate. +After #1663, current consumer/permanent authorities are synchronized to: + +- describe `ZIndex` as signed z-order / signed stacking order rather than relative placement; +- explicitly document that placement options revalidate default struct rectangle values; +- record #1662 as the RED regression witness and #1663 as the accepted GREEN functional head; +- preserve the final public API fingerprint and dependency graph. + +This pass is documentation-only. Its exact head must pass the complete nine-job PR matrix before merge. ## Compatibility guardrails @@ -250,15 +250,15 @@ Version 1.12.0 does not include: ## Release gate ```text -T120 accepted - -> T121 accepted - -> T122 accepted - -> T123 accepted / API frozen - -> T124 accepted - -> T125 accepted - -> T126 accepted - -> T127 stable candidate accepted (#1659) - -> closure-only record - -> final exact-head matrix - -> maintainer handoff +T120–T126 accepted + -> T127 original stable candidate #1659 green + -> closure-only #1660 green + -> pre-merge audit finds default-value edge case + -> RED #1662 + -> GREEN #1663 + -> documentation consistency pass + -> final exact-head nine-job matrix + -> maintainer handoff ``` + +After the final exact-head green result, merge, mainline Release validation, `v1.12.0` tagging, GitHub Release creation, and NuGet publication remain maintainer/release-workflow actions. From bc6611b8b9ab2ea201f7b337ad5372a578465558 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:47:42 -0400 Subject: [PATCH 61/63] docs: extend T127 closure with pre-merge hardening --- docs/T127-1.12.0-Release-Closure.md | 127 ++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 34 deletions(-) diff --git a/docs/T127-1.12.0-Release-Closure.md b/docs/T127-1.12.0-Release-Closure.md index df6362bfa..79ffb1950 100644 --- a/docs/T127-1.12.0-Release-Closure.md +++ b/docs/T127-1.12.0-Release-Closure.md @@ -7,13 +7,13 @@ ## Purpose -This document records the final T127 release-candidate evidence for `Icod.Terminal 1.12.0` and defines the last exact-head qualification step before maintainer handoff. +This document records the T127 stable-release evidence for `Icod.Terminal 1.12.0`, including the pre-merge hardening discovered after the first closure-only qualification. T127 does not widen the feature scope. The final public additions remain exactly the source-rectangle value contract plus nullable signed z-order on `TerminalRasterPlacementOptions`. -## Accepted release candidate +## Original accepted release candidate -Stable release metadata and synchronized release-facing authorities were qualified on exact head: +Stable release metadata and synchronized release-facing authorities were first qualified on exact head: ```text 0b7961f6d8151253be57f65a69e17a12ec4bdec5 @@ -25,7 +25,7 @@ Pull-request workflow: #1659 / 34717812704 ``` -The workflow completed successfully on all nine required jobs: +All nine required jobs passed: ```text Runtime Windows @@ -39,7 +39,7 @@ Package Stable 1.x release line Validated package artifact ``` -The validated package artifact was: +Validated package artifact: ```text artifact id: 10305651607 @@ -47,7 +47,7 @@ name: icod-terminal-pr-packages digest: sha256:ed5faa549819e89b35e03dc06a5e1061371a15fbd4336a46fbe25d6631408bbe ``` -The package-candidate artifact was: +Package-candidate artifact: ```text artifact id: 10305430874 @@ -55,9 +55,80 @@ name: icod-terminal-pr-package-candidate digest: sha256:700dac08d2a7655d77e113b5051391a05a88ef7cefc1c2c542841534adab37be ``` +The first closure-only head was: + +```text +ef02714bcbd9ab84572a7ed8200a0ad8071d831a +#1660 / 34718248621 +``` + +That exact head also passed all nine jobs. + +## Pre-merge audit finding + +A final documentation/sample/test audit found one value-type edge case before merge. + +`TerminalRasterSourceRectangle` is a `readonly struct`, so callers can produce: + +```csharp +default( TerminalRasterSourceRectangle ) +``` + +without invoking its validating public constructor. Before the hardening fix, the resulting zero-sized rectangle could pass placement-option validation and reach the persistent placement encoder. + +This did not change the intended public contract; it exposed a missing defensive validation path for a value the CLR can construct independently of the public constructor. + +## RED regression witness + +The deterministic test-only RED witness was established on: + +```text +f5f0a4f69d3571083f63654acd35b9893e8807dd +#1662 / 34720241497 +``` + +On the unfixed product, all three regression tests failed on every runtime TFM because the expected `ArgumentOutOfRangeException` was not thrown: + +```text +ResourceAwareValidationRejectsDefaultRectangle +EncoderRejectsDefaultRectangle +PublicCreateAndUpdateRejectDefaultRectangleBeforeOutput +``` + +The public create/update witness also proves the intended pre-output behavior rather than only exercising an internal helper. + +## GREEN hardening head + +The minimal production fix was accepted on exact head: + +```text +4d509c75decc37d280a92769fe668d3c6fbd41ce +``` + +Pull-request workflow: + +```text +#1663 / 34720415393 +``` + +All nine jobs passed. + +The fix: + +- gives `TerminalRasterSourceRectangle` one reusable intrinsic validator; +- uses that validator from the public constructor; +- makes `TerminalRasterPlacementOptions.Validate()` revalidate every present rectangle, including default struct values; +- therefore protects resource-aware validation and the direct encoder path without adding public API; +- keeps invalid public create/update operations pre-output; +- preserves all valid 1.11/1.12 placement bytes. + +The package/API freeze remained green, so the final public API fingerprint did not change. + +The same accepted head also normalized the executable persistent-raster sample and sample catalog to **signed z-order / stacking order**, avoiding terminology that could be confused with the separately excluded relative-placement graph feature. + ## Final public API freeze -The final deterministic public API fingerprint remains: +The deterministic public API fingerprint remains: ```text eed5fc18e5cdd1cdadf340ba37c3664a01fb9338c2080b709168606d51d934a8 @@ -70,7 +141,7 @@ docs/Public-API-Baseline-1.12.md docs/Public-API-Baseline-1.12.sha256 ``` -The only additive 1.12 public members are: +The only additive 1.12 public members remain: ```text TerminalRasterSourceRectangle @@ -83,7 +154,7 @@ TerminalRasterPlacementOptions.SourceRectangle TerminalRasterPlacementOptions.ZIndex ``` -No public API was added after T123. +No new `TerminalCapability` value or production dependency was introduced. ## Dependency and package boundary @@ -96,29 +167,14 @@ Icod.Timing 1.0.0 `Icod.TermInfo.Inspection` remains integration-test/sample-only and `Icod.TermInfo.Source` is not introduced into the production package graph. -Stable package metadata is `1.12.0` with no prerelease suffix. - -## Accepted tranche checkpoints - -```text -T120 architecture/API regret gate accepted -T121 table-driven TermInfo semantic evidence accepted -T122 source rectangle + resource-aware validation accepted -T123 signed z-order + final API freeze accepted -T124 placement encoder/acknowledged transaction aba1f7c0989d2c451294edf75590637c227a7e05 #1653 / 34713178166 -T125 adversarial/lifecycle/boundary hardening 799d096fa439c43b4f31399a551c4524fe40fa10 #1657 / 34716833678 -T126 sample/package/XML/downstream qualification 7b38994c1ba936df5887d1aa015394c4ed626ddf #1658 / 34717103814 -T127 stable release candidate 0b7961f6d8151253be57f65a69e17a12ec4bdec5 #1659 / 34717812704 -``` - -Every listed workflow from T124 onward passed the full nine-job PR matrix. +Stable package metadata remains `1.12.0` with no prerelease suffix. ## Frozen 1.12 behavior Version 1.12 adds two narrowly bounded placement controls: -- `SourceRectangle` selects a source-pixel crop which must fit completely inside the uploaded resource; -- `ZIndex` accepts the complete signed `int` range and expresses relative stacking intent. +- `SourceRectangle` selects a source-pixel crop which must satisfy its intrinsic scalar contract and fit completely inside the uploaded resource; +- `ZIndex` accepts the complete signed `int` range and expresses signed stacking order for the placement. The release preserves: @@ -137,24 +193,27 @@ The release deliberately excludes relative placement graphs, Unicode placeholder ## Consumer and downstream qualification -The accepted package qualification proves: +Package qualification proves: -- the backend-neutral persistent-raster sample creates and updates source crops and z-order; +- the backend-neutral persistent-raster sample creates and updates source crops and signed z-order; - fresh NuGet-only consumers compile/run `TerminalRasterSourceRectangle`, `SourceRectangle`, and `ZIndex` on all three supported TFMs; - generated XML documentation contains the new type, constructor, rectangle properties, and new placement-option properties on all three package TFMs; - current Stable 1.x `Icod.DCurses` acceptance/hardening soak remains green without a downstream code change. -## Final status-only qualification +## Final documentation consistency qualification -This closure record and the accompanying roadmap status changes are documentation-only. They do not change production code, package dependencies, package version, public API, or runtime behavior. +After the #1663 functional head, current release-facing/permanent authorities were synchronized to: -Before T127 is considered closed, the exact head containing this record must pass the same complete nine-job pull-request matrix one final time. +- document default-struct revalidation explicitly; +- describe `ZIndex` as signed z-order / signed stacking order rather than relative placement; +- preserve the distinction between z-order and the excluded relative-placement graph; +- record the RED #1662 and GREEN #1663 post-closure hardening evidence. -Only after that exact-head green result may PR #55 be marked ready for review. +These final authority updates are documentation-only. Their exact head must pass the same complete nine-job pull-request matrix before maintainer handoff. ## Maintainer handoff -After final exact-head PR qualification, the remaining release actions are intentionally left to the maintainer/release workflow: +After that final exact-head green result, the remaining release actions are intentionally left to the maintainer/release workflow: ```text merge PR #55 to main From c94d0f2b8e51e84bd12cbba9f13383e1c788ef07 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:48:19 -0400 Subject: [PATCH 62/63] docs: update current 1.12 qualification status --- Icod.Terminal-Development-Roadmap.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Icod.Terminal-Development-Roadmap.md b/Icod.Terminal-Development-Roadmap.md index 89c7a3100..30868b394 100644 --- a/Icod.Terminal-Development-Roadmap.md +++ b/Icod.Terminal-Development-Roadmap.md @@ -76,7 +76,7 @@ T123 z-order public contract acce T124 acknowledged create/update encoder integration accepted T125 lifecycle/adversarial/boundary hardening accepted T126 sample/package/downstream qualification accepted -T127 API freeze and stable release closure in progress +T127 API freeze and stable release closure accepted, with post-closure pre-merge hardening ``` The public 1.12 additions are exactly: @@ -104,9 +104,17 @@ Accepted implementation/qualification checkpoints: T124 aba1f7c0989d2c451294edf75590637c227a7e05 #1653 / 34713178166 T125 799d096fa439c43b4f31399a551c4524fe40fa10 #1657 / 34716833678 T126 7b38994c1ba936df5887d1aa015394c4ed626ddf #1658 / 34717103814 +T127 candidate + 0b7961f6d8151253be57f65a69e17a12ec4bdec5 #1659 / 34717812704 +T127 first closure + ef02714bcbd9ab84572a7ed8200a0ad8071d831a #1660 / 34718248621 +Pre-merge hardening + 4d509c75decc37d280a92769fe668d3c6fbd41ce #1663 / 34720415393 ``` -Each listed workflow passed the full nine-job PR matrix. +Each listed GREEN workflow passed the full nine-job PR matrix. + +The pre-merge audit also preserved the TDD RED witness for default-valued source rectangles at `f5f0a4f69d3571083f63654acd35b9893e8807dd`, workflow `#1662 / 34720241497`. The accepted fix revalidates every present `TerminalRasterSourceRectangle`, including `default(...)` values that bypass the public constructor, without changing the public API fingerprint. The production dependency graph remains: @@ -118,6 +126,7 @@ Icod.Timing 1.0.0 The versioned roadmap and release authorities are: - [`Icod.Terminal-1.12.0-Development-Roadmap.md`](Icod.Terminal-1.12.0-Development-Roadmap.md) +- [`docs/T127-1.12.0-Release-Closure.md`](docs/T127-1.12.0-Release-Closure.md) - [`docs/releases/1.12.0.md`](docs/releases/1.12.0.md) - [`docs/Persistent-Raster-Ownership.md`](docs/Persistent-Raster-Ownership.md) - [`docs/Public-API-Baseline-1.12.md`](docs/Public-API-Baseline-1.12.md) @@ -150,4 +159,6 @@ Advanced raster features beyond 1.12—relative placement graphs, Unicode placeh ## Maintainer handoff rule -For the 1.12 stable candidate, merge, mainline Release validation, `v1.12.0` tagging, GitHub Release creation, and NuGet publication remain maintainer/release-workflow actions after PR exact-head qualification and closure are complete. +The post-hardening release-facing documentation consistency pass is the final branch change. Its exact head must pass the complete nine-job PR matrix before merge. + +After that qualification, merge, mainline Release validation, `v1.12.0` tagging, GitHub Release creation, and NuGet publication remain maintainer/release-workflow actions. From cc0508d53e6c16133f638f9f71e65d4d805c6b65 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Sat, 12 Sep 2026 17:49:28 -0400 Subject: [PATCH 63/63] docs: record default rectangle validation in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7fb87d629..e8d0a6a81 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ if ( capability.IsUsable ) { Resource and placement identity is opaque. The public API does not expose Kitty image ids, image numbers, placement ids, raw APC commands, or backend selection. -`Columns` and `Rows` are independently optional and each supplied value is bounded to `1..16384`. `SourceRectangle` is measured in source-image pixels, must fit completely within the resource, and is validated before placement output. `ZIndex` is nullable and accepts the full signed 32-bit range. Placement still uses the terminal's current cursor location and does not move the text cursor; source cropping and z-order do not create an absolute layout or scene-graph contract. +`Columns` and `Rows` are independently optional and each supplied value is bounded to `1..16384`. `SourceRectangle` is measured in source-image pixels, must satisfy its intrinsic scalar contract, must fit completely within the resource, and is validated before placement output. Placement options revalidate every present rectangle, including `default(TerminalRasterSourceRectangle)` values that bypass the public constructor. `ZIndex` is nullable and accepts the full signed 32-bit range. Placement still uses the terminal's current cursor location and does not move the text cursor; source cropping and z-order do not create an absolute layout or scene-graph contract. Persistent identities are session-generation scoped. Explicit invalidation and lifecycle generation changes stale existing handles. Version 1.12 does not retain hidden raster copies for automatic replay or re-upload after suspend/resume uncertainty.