Repository navigation
feat(harness): add TesseraRuntime with Application, add TesseraHarness, and retire the Showcase - #64
Merged
Merged
Conversation
…ort slice, and record the catalog capability audit Record the owner's 2026-10-23 decisions in plan 036: Flow and ViewThatFits get their own slice before styling, the terminal colour-report probe becomes Slice 1 and runs in parallel, catalog requirements gain Focus, Pointer, Scroll, and Clock subsections that the capability slices work through, and the coverage walk reports levels, key-and-count routes, and stop reasons. Renumber the slices, add unattended-execution rules, update the developer tooling page, and add research record 043 with the audit's placements and drafted gap requirements for owner review.
Add the package-level seams Slice 2 of plan 036 lists, adapted from the seams spike patch to today's sources, with no new public API: - InMemoryTerminalDevice: package init(...keepsInputOpen:), deliverResize(to:), and closeInput(). An open device registers its byte and size-change streams when terminalDevice is read, so a resize delivered afterwards is never lost and late readers see no replayed history. - InMemoryTerminalSession: package init(device:) to wrap an open device. - TerminalSession: lastDrawn (buffer, cursor, sequence; set after a successful flush), size, and outputTap (teed before the draw's io.write); withApplicationTerminal(configuration:io:...) forwards probeImageID and probeSleep. - ViewGraph: a package init taking the clock the passes read. The seam tests live in TerminalSessionTests and ViewGraphClockTests.
Make TerminalSession.draw a nonisolated(nonsending) entry point, per the runtime isolation design in the Phase 5 spec proposal: the frame body runs in the caller's isolation, so a @mainactor host renders a non-Sendable graph where it lives, and the session serializes draws and commits the finished buffer on itself. sending is dropped from the body and the result. The draw splits into beginFrame (size and rendering policy, fixed once), the body in the caller's isolation, and commitFrame (encode, tee outputTap, write, flush, then set lastDrawn after a successful flush). The seam semantics are unchanged and nothing new is public. A @mainactor test host draws through an InMemoryTerminalDevice session; the same code did not compile against the sending signature.
# Conflicts: # Package.resolved
The draw body now runs in the caller's isolation, so the scope that owns the session can close between beginFrame and the commit. commitFrame checks that the session is open before it encodes, taps, or writes, so a draw caught in that window throws TerminalSessionError.closed instead of painting a frame onto the restored terminal. Document that deliverResize(to:) also emits where the public resize(to:) only changes the reported size, amend the Phase 5 spec's draw transaction and plan 036's Slice 2 text to match, and narrow the Delivers line to the main-actor host the test uses.
TesseraRuntime holds the app contract: Application refines only AnyObject, with body, apply(_:), and a defaulted isFinished, and its documentation carries the four adoption shapes. Tessera re-exports it, and TesseraRuntimeTests compiles each shape against import Tessera. TesseraHarness is a library product beside TesseraTestSupport. It carries the HeadlessRunner spike's driver, scenario parser, runner, report, and registry with its scenario corpus and tests, every public declaration now package. The spike stays under spikes/ as frozen evidence. Both targets get boundary allowlist rows and the shared swiftSettings, docs:targets and docs:merge know TesseraRuntime, and .gitattributes pins scenario files to LF.
Each view component ships its own demo instead of one monolithic app. Delete Examples/Sources/TesseraShowcase, its tests and Xcode schemes, the core:showcase task, and the manifest entries only the Showcase used, and point README.md, CONTRIBUTING.md, and docs/project-status.md at core:example.
…ement Check the Slice 3 box with its evidence, move Next: to Slice 4, record the copy-not-move, access, dependency, shim, and .gitattributes decisions the owner may want to weigh, add the discoveries, and update the changelog.
# Conflicts: # docs/plans/phase-4-view-layer-and-developer-tooling.md
…the unused CLI Qualify the Application docs and the spec: leaving @mainactor off an extension conformance is a compile error from another module and in plain Swift 6 mode, but a module with default main-actor isolation or InferIsolatedConformances infers the isolated conformance for its own types. Add Examples/Sources/ApplicationConsumer, a one-file target that imports only Tessera and declares an Application, so the Examples build fails if Tessera stops re-exporting TesseraRuntime. Remove the harness's CLI, which had no caller and no test; Slice 4 writes HarnessCLI fresh and the spike keeps the original. Record in plan Slice 4 that its driver snapshot test restores the graph-to-session-to-bytes coverage the Showcase test gave, and note the three findings under Discoveries.
A scratch two-module SwiftPM package on Swift 6.3.3 and 6.4 settled when a bare extension conformance on a main-actor type compiles: it is an error in plain Swift 6 mode, builds with default main-actor isolation in the extension's module, and builds under InferIsolatedConformances (on 6.3.3 only for a type declared @mainactor, not one isolated through its module's default isolation). Rewrite the Application docs, the spec paragraph, and the plan's Discoveries and Slice 4 Verify to say so, run Slice 4's second fixture in plain Swift 6 mode, and describe ShotArtifact without the removed CLI.
robfeldmann
commented
Oct 24, 2026
…ntime # Conflicts: # CHANGELOG.md # docs/plans/phase-4-view-layer-and-developer-tooling.md
Sort the root and Examples manifests alphabetically, state enduring facts in comments and DocC instead of plan numbers, conform Locator and FrameExpectation to CustomStringConvertible, say what Application's class-only contract means, express the scenario tests as snapshots, and record the owner's approval of the Slice 3 decisions.
robfeldmann
marked this pull request as ready for review
October 24, 2026 20:39
robfeldmann
added a commit
that referenced
this pull request
Oct 24, 2026
Slice 4 deletes ApplicationConsumer and moves the harness's demos and registry; merging the squashed #64 re-added them.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Plan 036 Slice 3, stacked on #59 (base
phase-4/02-seams, merged with its head08174d2). Draft: Slice 4 builds on it.What changes
TesseraRuntime(new target, re-exported byTessera):Application: AnyObjectwithbody,apply(_:), and a defaultedisFinished, per the spec's Runtime isolation section and Decision 9 (i). No@MainActor, noSendableMetatype. Its DocC carries the four adoption shapes.Tests/TesseraRuntimeTestscompiles a plain class, an@Observableclass, a@MainActor final class ...: @MainActor Application, andextension Counter: Application {}; the default-main-actor shape stays pinned by Slice 4's fixtures.TesseraHarness(new library product besideTesseraTestSupport): the HeadlessRunner spike's driver, scenario parser, runner, report, key names, locators, registry, and two fixtures, with its 18 tests andScenarios/*.scn(byte-identical). Everypublicbecamepackage, so the target declares no public name.DemoAppandDemoRegistrystand in forApplicationandApplicationRegistryuntil Slice 4.scripts/check-package-boundaries.py(one unit test added), the sharedswiftSettings, anddocs:targets/docs:mergeknowTesseraRuntime.Examples/Sources/TesseraShowcase, its tests and Xcode schemes,core:showcase, the manifest entries only it used, and its mentions inREADME.md,CONTRIBUTING.md,docs/project-status.md, and the changelog.Examples/Sources/ApplicationConsumer: a one-file target that imports onlyTesseraand declares anApplication, so the Examples build in CI fails ifTesserastops re-exportingTesseraRuntime..gitattributespins*.scnto LF;docs/architecture/developer-tooling.mdmarksApplicationbuilt; both spike READMEs point at the maintained copy.Verification
mise run core:test: 700 tests green on Xcode 26.6 (676 on the base, plus 6 runtime and 18 harness tests); the same 700 on Xcode 27.1.mise run quality:format,quality:lint(includesquality:architecture), anddocs:lintgreen;swift buildandswift test --package-path Examplesgreen (13 tests).git diff --name-only origin/phase-4/02-seams...HEADlists noPackage.resolved.53b7a25) and 40060342201 (merged heade05d6e9) were green on every leg, Windows and the static Linux SDK included; the critic-fix head2c28bb6(run 40061964424) was green too, and the wording-fix head096cec9is running.Critic review fixes (
2c28bb6)Applicationdocs and the spec now state the rule by compiler mode. A bareextension Notes: Application {}on a main-actor type is a compile error in plain Swift 6 mode, builds with default main-actor isolation in the extension's module, and builds underInferIsolatedConformances(on Swift 6.3.3 only for a type declared@MainActor, not one isolated through its module's default isolation; Swift 6.4 infers both). Checked with a scratch two-module SwiftPM package on 6.3.3 and 6.4; Slice 4's second fixture runs in plain Swift 6 mode (096cec9).ApplicationConsumerguards the cross-packageimport Tessera; Slice 4's Verify line records that its driver snapshot test restores the graph-to-session-to-bytes coverage the Showcase test gave.CLIis removed; the spike keeps the original and Slice 4 writesHarnessCLIfresh.Decisions for the owner
All recorded in the plan under Decisions and Discoveries; the reversible ones are marked "(owner to weigh)":
spikes/HeadlessRunnerwould delete itsPackage.resolved. The spike stays as frozen evidence.sources:shims. The moved code is Foundation only, so there was no divergent platform code to isolate; the first seam arrives with Slice 4's live runner..gitattributesearlier than Slice 5. A"\r\n"is one SwiftCharacter, so a CRLF checkout would read a whole scenario as one line.Tessera;TesseraRuntimeis a target, not a product.