Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
79da704
docs(plans): settle Phase 4 decisions 2, 5, and 8, add the colour-rep…
robfeldmann Oct 23, 2026
ea513af
feat(terminal): add the package seams the headless harness drives
robfeldmann Oct 23, 2026
46f0d65
docs(plans): record Phase 4 Slice 2 evidence, seam decisions, and dis…
robfeldmann Oct 23, 2026
0bb02e7
refactor(terminal): draw in the caller's isolation
robfeldmann Oct 24, 2026
7ef4b92
Merge remote-tracking branch 'origin/main' into tmp-merge-58
robfeldmann Oct 24, 2026
0539b57
Merge origin/docs/phase-4-decisions into phase-4/02-seams
robfeldmann Oct 24, 2026
05df29c
Merge origin/main into phase-4/02-seams
robfeldmann Oct 24, 2026
7d9eb46
Merge origin/main into phase-4/02-seams
robfeldmann Oct 24, 2026
08174d2
fix(terminal): reject a draw that commits after its session closed
robfeldmann Oct 24, 2026
38cc7fd
feat(harness): add TesseraRuntime with Application and TesseraHarness
robfeldmann Oct 24, 2026
53b7a25
chore(examples): retire the Showcase
robfeldmann Oct 24, 2026
e5de1ab
docs(plans): record Slice 3, the runtime, harness, and Showcase retir…
robfeldmann Oct 24, 2026
e05d6e9
Merge origin/phase-4/02-seams into phase-4/03-harness-runtime
robfeldmann Oct 24, 2026
2c28bb6
fix(harness): qualify the isolation docs, guard import Tessera, drop …
robfeldmann Oct 24, 2026
096cec9
docs(harness): state the isolation rule by compiler mode, not by module
robfeldmann Oct 24, 2026
338bd6f
feat(harness): add the application registry, the main-actor driver, a…
robfeldmann Oct 24, 2026
b2ee4f1
docs(plans): record Slice 4, the registry, viewports, driver, and tes…
robfeldmann Oct 24, 2026
e707aa0
docs(plans): record the Windows leg of Slice 4's CI
robfeldmann Oct 24, 2026
40fea7d
fix(harness): address the Slice 4 review nits
robfeldmann Oct 24, 2026
3faa2f4
feat(harness): add the scenario language, runner, and tessera run
robfeldmann Oct 24, 2026
ef4592b
test(harness): read the README table by scalar lines so a CRLF checko…
robfeldmann Oct 24, 2026
5b4dabf
docs(plans): record Slice 5, the scenario language, runner, tessera r…
robfeldmann Oct 24, 2026
4868e84
fix(harness): address the Slice 5 review findings
robfeldmann Oct 24, 2026
c979401
docs(harness): word the scenario finish rule so a later expect finish…
robfeldmann Oct 24, 2026
ee30fd7
Merge remote-tracking branch 'origin/main' into phase-4/03-harness-ru…
robfeldmann Oct 24, 2026
8eb1466
fix(harness): address review feedback on the runtime and harness
robfeldmann Oct 24, 2026
d37e358
Merge remote-tracking branch 'origin/phase-4/03-harness-runtime' into…
robfeldmann Oct 24, 2026
86a1cb5
refactor(harness): apply review conventions to the driver and registry
robfeldmann Oct 24, 2026
a4a0183
Merge remote-tracking branch 'origin/phase-4/04-driver' into phase-4/…
robfeldmann Oct 24, 2026
52d4bbc
refactor(harness): apply review conventions to the scenario runner
robfeldmann Oct 24, 2026
3041a7b
refactor(harness): spell the scratch directory with forward slashes
robfeldmann Oct 24, 2026
2c463f5
docs(skills): split the devtools skill into a published tessera skill…
robfeldmann Oct 24, 2026
d2f74d5
Merge remote-tracking branch 'origin/phase-4/04-driver' into phase-4/…
robfeldmann Oct 24, 2026
8a8ccc1
fix(harness): keep tessera help free of repository paths
robfeldmann Oct 24, 2026
324f760
feat(harness): declare the tessera command line with swift-argument-p…
robfeldmann Oct 24, 2026
4bc3717
feat(dev): explain dev:launch without a name and complete its app names
robfeldmann Oct 24, 2026
361a6e3
Merge remote-tracking branch 'origin/phase-4/04-driver' into phase-4/…
robfeldmann Oct 24, 2026
762e2b6
refactor(harness): declare run, shot, and tree with swift-argument-pa…
robfeldmann Oct 24, 2026
c02a1ab
feat(dev): explain dev:run, dev:shot, and dev:tree without arguments …
robfeldmann Oct 24, 2026
04ef8a5
fix(dev): build tessera once per dev task and run the binary
robfeldmann Oct 24, 2026
9d002c1
Merge remote-tracking branch 'origin/phase-4/04-driver' into phase-4/…
robfeldmann Oct 24, 2026
a6a414a
fix(dev): run dev:help through the shared quiet build
robfeldmann Oct 24, 2026
dd6ac69
fix(harness): keep help with an extra word a usage error and test com…
robfeldmann Oct 24, 2026
ee42d75
fix(dev): exit 2 from a dev task given no argument unless stdout is a…
robfeldmann Oct 24, 2026
e0366bf
Merge remote-tracking branch 'origin/phase-4/04-driver' into phase-4/…
robfeldmann Oct 24, 2026
1b6efc0
fix(harness): refuse a repeated --repeat again and test shot and tree…
robfeldmann Oct 24, 2026
747322a
fix(dev): exit 2 from dev:run, dev:shot, and dev:tree without argumen…
robfeldmann Oct 24, 2026
233dc43
fix(harness): refuse a repeated single-value option before ArgumentPa…
robfeldmann Oct 24, 2026
b710b88
Merge remote-tracking branch 'origin/main' into phase-4/04-driver
robfeldmann Oct 24, 2026
a3cbacc
chore: drop files the squash merge of #64 re-added
robfeldmann Oct 24, 2026
dac0a8e
Merge remote-tracking branch 'origin/phase-4/04-driver' into phase-4/…
robfeldmann Oct 24, 2026
3b7a7b4
Merge remote-tracking branch 'origin/main' into phase-4/05-scenarios
robfeldmann Oct 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions .agents/skills/tessera-contributing/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
---
name: tessera-contributing
description: Use Tessera's own demos and developer tools while working in this repository. Use when running `mise run dev:*`, launching or snapshot-testing a demo, adding a demo, or fixing a library defect a demo shows. Read with the `tessera` skill, which it extends.
version: 0.1.0
description: Use Tessera's own demos, scenario corpus, and developer tools while working in this repository. Use when running `mise run dev:*`, launching, picturing, or snapshot-testing a demo, adding a demo and its scenario, running `Examples/Scenarios`, or fixing a library defect a demo shows. Read with the `tessera` skill, which it extends.
version: 0.2.0
---

# Contributing to Tessera with the developer tools

Everything in the [`tessera` skill](../tessera/SKILL.md) applies here: the `tessera` commands, the exit codes, and the pty recipe. This skill adds what only a Tessera contributor has: the `mise` tasks, the demos, and the demo snapshot tests. [Developer tooling](../../../docs/architecture/developer-tooling.md) is the design behind the tools.
Everything in the [`tessera` skill](../tessera/SKILL.md) applies here: the `tessera` commands, the scenario grammar, goldens, the exit codes, and the pty recipe. This skill adds what only a Tessera contributor has: the `mise` tasks, the demos, the scenario corpus, and the demo snapshot tests. [Developer tooling](../../../docs/architecture/developer-tooling.md) is the design behind the tools.

## Commands

| mise | tessera | Does |
| --- | --- | --- |
| `mise run dev:apps` | `tessera apps` | List the registered demos: name, minimum size, viewports. |
| `mise run dev:apps --json` or `--pretty` | `tessera apps --json` or `--pretty` | Force the JSON object or the text table. |
| `mise run dev:launch NAME` | `tessera launch NAME` | Run one demo in this terminal until it quits. |
| `mise run dev:launch` | `tessera help launch`, then `tessera apps` | With no name, print the command's help and the demos. At a terminal that is the answer and the task exits 0; in a pipe or a script nothing ran and it exits 2, as `tessera` does. |
| `mise run dev:launch`, `dev:shot`, or `dev:tree` | `tessera help COMMAND`, then `tessera apps` | With no name, print the command's help and the demos. At a terminal that is the answer and the task exits 0; in a pipe or a script nothing ran and it exits 2, as `tessera` does. `dev:run` with no path prints its help the same way. |
| `mise run dev:run PATH...` | `tessera run PATH...` | Run scenario files or directories of them, with `--record`, `--repeat N`, and `--timing`. |
| `mise run dev:shot NAME [SIZE]` | `tessera shot NAME [SIZE]` | Print one demo's screen as text. |
| `mise run dev:tree NAME [SIZE]` | `tessera tree NAME [SIZE]` | Print one demo's node tree. |
| `mise run dev:help [COMMAND]` | `tessera help [COMMAND]` | Print the commands, or one command's usage. |

`mise run dev:*` builds the `tessera` executable once, quietly (`swift build --quiet --package-path Examples --product tessera`), and runs the binary it built, so a build that succeeds prints nothing and one that fails still prints its errors; the binary keeps the task's terminal. It is the product of `Examples/Sources/TesseraDev`, whose registry is `Demos.registry()` from `Examples/Sources/DemoSupport`, so the apps it lists are Tessera's demos.
`mise run dev:*` builds the `tessera` executable once, quietly (`swift build --quiet --package-path Examples --product tessera`), and runs the binary it built, so a build that succeeds prints nothing and one that fails still prints its errors; the binary keeps the task's terminal, and `--json` and `--pretty` go through to it. It is the product of `Examples/Sources/TesseraDev`, whose registry is `Demos.registry()` from `Examples/Sources/DemoSupport`, so the apps it lists are Tessera's demos.

With mise's shell completions enabled, `mise run dev:launch <Tab>` offers the demo names. Completion never builds: `scripts/dev-app-names.sh` asks the `Examples/.build/debug/tessera` that an earlier `dev:*` run built, and offers nothing before there is one. Run `mise run dev:apps` once after a fresh checkout, or after registering a demo. To complete `tessera` itself in your shell, see the `tessera` skill.
With mise's shell completions enabled, `mise run dev:launch <Tab>` offers the demo names, `dev:shot` and `dev:tree` offer them too and then the sizes, and `dev:run` offers `.scn` files and directories. Completion never builds: `scripts/dev-app-names.sh` asks the `Examples/.build/debug/tessera` that an earlier `dev:*` run built, and offers nothing before there is one. Run `mise run dev:apps` once after a fresh checkout, or after registering a demo. To complete `tessera` itself in your shell, see the `tessera` skill.

## Run a demo

`mise run dev:launch counter` opens the counter in your terminal. Each demo quits on `q`, matched by `isQuitKey` in `Examples/Sources/DemoSupport`. An agent without a terminal uses the pty recipe from the `tessera` skill with `mise run dev:launch counter` in place of `swift run tessera launch counter`; a headless snapshot test is the cheaper way to read a screen.
`mise run dev:launch counter` opens the counter in your terminal. Each demo quits on `q`, matched by `isQuitKey` in `Examples/Sources/DemoSupport`. An agent without a terminal uses the pty recipe from the `tessera` skill with `mise run dev:launch counter` in place of `swift run tessera launch counter`; `mise run dev:shot` or a scenario is the cheaper way to read a screen.

## The scenario corpus

`Examples/Scenarios` holds Tessera's scenarios, `NAME.scn` with its goldens in `NAME/` beside it. `mise run dev:run Examples/Scenarios --pretty` runs all of it, as CI does on macOS, Linux, and Windows, and `ScenarioCorpusTests` in `Examples/Tests/DemoSupportTests` runs the same files through `ScenarioRunner`. `Examples/Scenarios/README.md` is the grammar's source; the `tessera` skill carries the part an app author needs, so a change to the grammar updates both. A new `shot` needs one run with `--record`, and the golden it writes is reviewed and committed with the scenario. The corpus is the examples to copy: `counter-basics.scn` drives input, `expect screen`, and a `shot`.

## Snapshot-test a demo

Expand All @@ -39,18 +46,19 @@ func `the counter counts`() async throws {
}
```

`withHeadlessHost` opens a terminal session over a headless device, steps the demo through the same driver as `launch`, and feeds every frame to one Ghostty virtual terminal, so `host.screenRows` is the screen a terminal would show. `@testable import TesseraHarness` reaches it; it is not API for app authors. The viewport names are the ones the `tessera` skill lists.
`withHeadlessHost` opens a terminal session over a headless device, steps the demo through the same driver as `launch`, and feeds every frame to one Ghostty virtual terminal, so `host.screenRows` is the screen a terminal would show. `@testable import TesseraHarness` reaches it; it is not API for app authors, who run their scenarios through the public `ScenarioRunner`. The viewport names are the ones the `tessera` skill lists.

## Add a demo

1. Write a `final class` conforming to `Demo` in `Examples/Sources/DemoSupport`: a kebab-case `name`, a `minimum` size, `viewports`, and `fixtures` (empty when there are none). None has a default.
2. Handle the quit key in `apply(_:)` with `isQuitKey`, and report it through `isFinished`.
3. Register it with one line in `Demos.registry()`.
4. Add its snapshot test beside the counter's.
4. Add its scenario to `Examples/Scenarios` and a snapshot test beside the counter's.

A demo is interactive and shows every style or variant that can differ. It uses only what the library provides.

## Rules

- When a demo shows a library defect, fix the library and add a regression test. Never bend the demo to hide it.
- A name registered twice fails every command that reads the registry. Never rely on the later registration winning.
- `--record` writes goldens from what the demo does now; it is for a new `shot` and a reviewed, intended change, never for a failing run you do not understand.
Loading
Loading