Skip to content

feat: default semantic rendering to libghostty-vt#157

Merged
ThomasK33 merged 3 commits into
mainfrom
vt-impl-5ds6
Jun 16, 2026
Merged

feat: default semantic rendering to libghostty-vt#157
ThomasK33 merged 3 commits into
mainfrom
vt-impl-5ds6

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

Summary

Make libghostty-vt the default semantic renderer for snapshots, render waits, hashes, batch render-wait steps, and host semantic replay when the optional native backend is available. Keep ghostty-web as the default visual renderer for PNG screenshots and WebM exports.

Behavior changes

  • Automatic defaults now split semantic and visual renderer selection:
    • semantic actions prefer libghostty-vt and fall back to ghostty-web when native support is unavailable;
    • visual artifacts default to ghostty-web.
  • Explicit renderer selection through --renderer, AGENT_TTY_RENDERER, or Home config.json.defaultRenderer still overrides both semantic and visual defaults.
  • Host inspect/runtime metadata now reports the active backend dynamically.
  • Capability reporting now distinguishes semantic-render availability from browser-backed visual capability, with legacy wait modes degraded rather than fully unavailable when only render waits lack a renderer.
  • Review fixes keep host renderer operations leased during backend use and avoid leaking automatic private host renderer defaults into spawned PTY environments.

Dogfooding

  • Added proof bundle: dogfood/20260616-default-semantic-renderer/
  • Includes JSON envelopes, artifact manifests, PNG screenshots, WebM recordings, asciicast recording, checksums, file metadata, and replay script.
  • Reviewer checks are documented in dogfood/20260616-default-semantic-renderer/README.md.

Validation

  • npm run format:check
  • npm run lint
  • npm run typecheck
  • npx vitest run test/unit/host/renderer.test.ts test/unit/pty/createPty.test.ts test/unit/renderer/capabilities.test.ts test/unit/renderer/libghosttyVtBackend.test.ts test/unit/commands/wait.test.ts
  • npx vitest run --maxWorkers=1 test/integration/backend-selection.test.ts
  • npx vitest run --maxWorkers=1 test/integration/host-renderer-rpc.test.ts
  • npm run test
  • npm run build
  • npm run smoke:install -- --skip-build
  • npm run verify
  • bash dogfood/20260616-default-semantic-renderer/commands.sh

Note: npm run test:integration -- test/integration/backend-selection.test.ts test/integration/host-renderer-rpc.test.ts exceeded the 240s local workspace timeout when run in parallel; the same two files passed individually with --maxWorkers=1.


📋 Implementation Plan

Implementation Plan: Make libghostty-vt the Default Semantic Renderer

Executive summary

Make libghostty-vt the default semantic renderer for render waits, snapshots, screen hashing, batch render-wait steps, and the host's internal semantic replay work, while keeping ghostty-web as the default visual renderer for PNG screenshots and WebM exports.

Do not flip the single global DEFAULT_RENDERER_NAME from ghostty-web to libghostty-vt. The safer implementation is a small default-resolution split:

  • If the user explicitly chooses a renderer with --renderer, AGENT_TTY_RENDERER, or config.json.defaultRenderer, respect that choice as the requested renderer for both semantic and visual operations; WebM remains a special case where a requested libghostty-vt is accepted but normalized to ghostty-web because the actual video producer is browser-backed.
  • If the user does not choose a renderer:
    • semantic operations prefer libghostty-vt when @coder/libghostty-vt-node is available;
    • semantic operations fall back to ghostty-web when the optional native package is unavailable;
    • visual operations default directly to ghostty-web to avoid native-addon dependency and double boot/replay overhead.

This preserves the existing visual proof contract while moving the common agent loop (runwaitsnapshot) to the faster in-process renderer on supported platforms.

Evidence and constraints verified

  • src/renderer/names.ts currently defines RendererNameSchema = ['ghostty-web', 'libghostty-vt'] and DEFAULT_RENDERER_NAME = 'ghostty-web'.
  • src/cli/context.ts currently resolves one CommandContext.rendererDefault from --renderer, AGENT_TTY_RENDERER, config.json.defaultRenderer, then DEFAULT_RENDERER_NAME.
  • src/cli/main.ts stores the resolved semantic renderer in process.env.AGENT_TTY_RENDERER; the per-session host uses that environment variable when no renderer is provided in RPC params.
  • snapshot, wait, and batch consume context.rendererDefault for semantic renderer work.
  • screenshot and record export --format webm also consume context.rendererDefault today, even though their default should remain visual/browser-backed.
  • LibghosttyVtBackend.screenshot() delegates to an internal GhosttyWebBackend fallback after replaying libghostty-vt; using it as the screenshot default would do avoidable native boot/replay plus browser boot/replay.
  • generateWebmExport() already maps a requested libghostty-vt renderer to ghostty-web before constructing the video backend, so WebM would not hard-fail from a global flip, but direct ghostty-web visual default is still simpler and avoids misleading intent.
  • @coder/libghostty-vt-node is an optional dependency. src/renderer/readiness.ts already exposes probeLibghosttyVt() and actionable dashboard-readiness errors.
  • doctor --json in this workspace reports both browser-backed screenshot/WebM and libghostty-vt dashboard capability as available.
  • docs/adr/0006-session-dashboard-follows-event-log.md already justifies libghostty-vt for fast in-process semantic replay and rejects ghostty-web for the dashboard due to Chromium dependency and multi-second boot.
Explore-agent review summary

Three read-only Explore lanes inspected code paths, tests, docs, and dogfood references. Two lanes recommended the semantic-vs-visual split because of optional native dependency risk and visual-command overhead. One lane noted that a raw global default flip would be mechanically viable because screenshot and WebM have fallback/mapping paths, but that approach still changes visual defaults and makes missing-native behavior worse. The final plan adopts the semantic-vs-visual split because it matches the product goal from the original discussion and is the least surprising rollout.

Target behavior

Default behavior when no renderer is configured

Operation Default backend when libghostty-vt is available Default backend when libghostty-vt is unavailable Notes
wait render conditions libghostty-vt ghostty-web Includes text/regex/screen-stable/cursor render waits.
snapshot libghostty-vt ghostty-web Snapshot artifact metadata records the actual backend used.
batch render-wait steps libghostty-vt ghostty-web The batch driver receives the semantic default.
host internal semantic replay libghostty-vt ghostty-web Used by host-side run-completion replay work through AGENT_TTY_RENDERER.
dashboard libghostty-vt required unavailable with actionable error Existing dashboard behavior remains intentionally stricter.
screenshot ghostty-web ghostty-web Avoids LibghosttyVtBackend.screenshot() double replay by default.
record export --format webm ghostty-web ghostty-web Keeps video artifact producer explicit.
record export --format asciicast no renderer no renderer No change.

Semantic fallback to ghostty-web is best effort: it still requires the browser-backed renderer stack to be usable. doctor --json and capability discovery should surface a clear unavailable/degraded state when neither libghostty-vt nor ghostty-web can satisfy render replay.

Explicit renderer behavior

If a renderer is explicitly configured by root --renderer, AGENT_TTY_RENDERER, or Home config.json.defaultRenderer, keep the existing mental model: that chosen renderer is the default renderer for all commands.

Examples:

  • agent-tty --renderer ghostty-web snapshot ... uses ghostty-web even when native is available.
  • agent-tty --renderer libghostty-vt screenshot ... is allowed and continues to produce a PNG through LibghosttyVtBackend's honest ghostty-web fallback metadata.
  • AGENT_TTY_RENDERER=libghostty-vt or config.json with { "defaultRenderer": "libghostty-vt" } intentionally routes screenshots through the native/fallback path too; document that this requires native availability and may add double replay/boot overhead for screenshots. For WebM, the request is accepted but generateWebmExport() still normalizes the actual producer to ghostty-web and metadata must say ghostty-web.
  • AGENT_TTY_RENDERER=ghostty-web restores legacy behavior globally.
  • config.json with { "defaultRenderer": "ghostty-web" } restores legacy behavior for that Home.

Implementation phases

Phase 1 — Introduce explicit semantic/visual defaults

Files:

  • src/renderer/names.ts
  • src/cli/context.ts
  • test/unit/renderer/registry.test.ts
  • test/unit/cli/context.test.ts

Plan:

  1. Keep DEFAULT_RENDERER_NAME: RendererName = 'ghostty-web' for compatibility in code paths that still need a single safe renderer fallback, but add a code comment making clear it is no longer the product's semantic default. Treat it as the legacy/safe single-renderer fallback only.
  2. Add named constants in src/renderer/names.ts:
    export const DEFAULT_SEMANTIC_RENDERER_NAME: RendererName = 'libghostty-vt';
    export const DEFAULT_VISUAL_RENDERER_NAME: RendererName = 'ghostty-web';
  3. Keep src/renderer/names.ts otherwise pure and easy to import. Put any native probing in CLI/context or readiness helpers, not in the schema/constant module.
  4. In src/cli/context.ts, add a small resolver for renderer defaults. Recommended shape:
    interface RendererDefaultResolutionDeps {
      probeLibghosttyVt?: typeof probeLibghosttyVt;
    }
    
    interface RendererDefaults {
      semantic: RendererName;
      visual: RendererName;
    }
  5. Resolve renderer defaults as follows:
    • Compute configuredRenderer = options.renderer ?? env.AGENT_TTY_RENDERER ?? configFile?.defaultRenderer.
    • If configuredRenderer !== undefined, validate it with resolveRendererDefault(configuredRenderer) and set both semantic and visual defaults to the validated value.
    • If configuredRenderer === undefined, use a process-memoized, non-fatal probeLibghosttyVt() result. Set semantic to libghostty-vt when available or ghostty-web when unavailable, and set visual to ghostty-web.
    • Probe failures must never fail context resolution for automatic defaults; they should be treated as native unavailable. Explicit --renderer libghostty-vt should still validate as a supported renderer name and then fail later with the existing actionable boot/readiness error if the native module cannot load.
  6. Extend CommandContext with a visual default while preserving the existing semantic field name to minimize churn:
    readonly rendererDefault: RendererName; // semantic default
    readonly rendererVisualDefault: RendererName;
  7. Keep resolveRendererDefault(raw?: string) as the synchronous validator used for explicit values and tests.
  8. Add or adjust assertions/invariants so impossible resolver states crash in tests rather than silently falling through:
    • configured renderer validates before use;
    • probe fallback returns only allowed RendererName values;
    • visual default is non-empty and schema-valid.
  9. Run rg -n "rendererDefault|rendererVisualDefault|DEFAULT_RENDERER_NAME" src test during implementation and update every fixture/consumer deliberately; do not rely only on the file list in this plan.

Quality gate after Phase 1:

npm run test:unit -- test/unit/cli/context.test.ts test/unit/renderer/registry.test.ts
npm run typecheck

Expected test work:

  • Update cached CommandContext fixtures to include rendererVisualDefault.
  • Add deterministic tests for native-available and native-unavailable automatic semantic resolution. Prefer dependency injection into resolveCommandContext if small; otherwise mock probeLibghosttyVt() in the test file.
  • Add tests proving the automatic native probe is memoized and that probe rejection/failure falls back to ghostty-web without failing context resolution.
  • Preserve tests proving explicit flag/env/config precedence.
  • Add tests proving visual default is ghostty-web only when no renderer was explicitly configured, and that explicit env/config libghostty-vt intentionally makes screenshot take the native/fallback route while explicit WebM still reports ghostty-web as the actual producer.

Phase 2 — Route commands to the correct default

Files:

  • src/cli/commands/snapshot.ts
  • src/cli/commands/wait.ts
  • src/cli/commands/batch.ts
  • src/cli/commands/screenshot.ts
  • src/cli/commands/record-export.ts
  • src/cli/main.ts
  • related unit tests under test/unit/commands/

Plan:

  1. Leave semantic commands on context.rendererDefault:
    • snapshot live RPC and offline replay;
    • wait render conditions and offline render wait;
    • batch render-wait driver.
  2. Change visual commands to use context.rendererVisualDefault:
    • screenshot live RPC and offline replay;
    • record export --format webm's generateWebmExport({ rendererName }) input.
  3. Leave record export --format asciicast unchanged.
  4. Keep src/cli/main.ts setting process.env.AGENT_TTY_RENDERER = context.rendererDefault because the host's environment default is a semantic renderer default. Update debug logging to include both semantic and visual defaults if that can be done without noisy output changes.
  5. Update the root --renderer help text in src/cli/main.ts so it no longer implies one universal default. Suggested wording: Renderer backend override. Defaults vary by command: semantic actions prefer libghostty-vt when available; visual artifacts default to ghostty-web.
  6. Do not add a new public environment variable unless implementation proves it necessary; CLI requests pass explicit renderer names to visual RPC operations.
  7. Update command unit-test context fixtures to include both renderer defaults.
  8. Add/adjust tests proving create-spawned hosts inherit the semantic default through AGENT_TTY_RENDERER. Direct runHost() without this env may keep the legacy DEFAULT_RENDERER_NAME/ghostty-web fallback because direct host invocation is internal and not the supported CLI path; document this decision in code comments or tests if it is non-obvious.

Quality gate after Phase 2:

npm run test:unit -- \
  test/unit/commands/snapshot.test.ts \
  test/unit/commands/wait.test.ts \
  test/unit/commands/batch.test.ts \
  test/unit/commands/screenshot.test.ts \
  test/unit/commands/record-export.test.ts \
  test/unit/commands/version.test.ts \
  test/unit/export/webm.test.ts \
  test/unit/host/hostMain.test.ts \
  test/unit/host/renderer.test.ts
npm run typecheck

Expected assertions:

  • Snapshot/wait/batch pass the semantic default to RPC/offline replay.
  • Screenshot passes the visual default to RPC/offline replay.
  • WebM export receives the visual default by default, while explicit libghostty-vt still maps to ghostty-web inside generateWebmExport().

Phase 3 — Update runtime introspection and version reporting

Files:

  • src/cli/commands/version.ts
  • src/renderer/capabilities.ts
  • src/renderer/readiness.ts if helper wording/types need reuse
  • test/unit/commands/version.test.ts
  • test/unit/renderer/capabilities.test.ts
  • test/unit/renderer/readiness.test.ts
  • test/integration/cli.test.ts

Plan:

  1. Update buildVersionResult().rendererBackends from ['ghostty-web'] to ['ghostty-web', 'libghostty-vt'] or RendererNameSchema.options if importing that list does not create an awkward dependency.
  2. Make capability discovery reflect the new default truth instead of continuing to describe render snapshot/wait as purely built-in. Preferred behavior:
    • snapshot is available when at least one semantic renderer is usable (libghostty-vt available, or ghostty-web/Playwright usable enough for render replay), otherwise unavailable with a semantic-renderer reason.
    • render-dependent wait is available under the same condition; non-render waits (--exit, --idle-ms) remain operational, but the current coarse wait capability should be honest about render waits because that is the documented capability.
    • Preserve the existing quick/full discovery distinction: quick checks must not unexpectedly launch Chromium just to decide semantic availability; full checks may use the existing deeper renderer/browser smoke checks.
    • record-export-asciicast remains built-in.
    • screenshot and record-export-webm remain browser-backed visual capabilities.
  3. If changing capability semantics is too broad during implementation, make that deferral explicit in docs/tests and keep the old behavior only as a deliberate compatibility decision. The preferred plan is to update capabilities now so doctor --json matches actual default renderer behavior.
  4. Keep version --json capability discovery best-effort. The dashboard capability already reports libghostty-vt availability via probeLibghosttyVt().
  5. Do not change the JSON envelope shape. Only the backend list contents, capability statuses/reasons, and actual rendererBackend values change.
  6. Review doctor --json output manually after implementation to ensure it clearly distinguishes:
    • semantic renderer availability for snapshot/render-wait;
    • browser-backed visual features;
    • dashboard/native availability.

Quality gate after Phase 3:

npm run test:unit -- \
  test/unit/commands/version.test.ts \
  test/unit/renderer/capabilities.test.ts \
  test/unit/renderer/readiness.test.ts
npm run test:integration -- test/integration/cli.test.ts
npx tsx src/cli/main.ts version --json | jq '.result.rendererBackends, .result.capabilities'
npx tsx src/cli/main.ts doctor --json | jq '.result.capabilities'

Phase 4 — Update integration and e2e coverage

Files/tests to inspect and update:

  • test/integration/backend-selection.test.ts
  • test/integration/host-renderer-rpc.test.ts
  • test/integration/cross-backend-screen-hash.test.ts
  • test/integration/wait-render.test.ts
  • test/e2e/libghostty-vt-renderer.test.ts
  • test/e2e/hello-prompt.test.ts
  • test/e2e/renderer-errors.test.ts

Plan:

  1. Add a helper in tests to detect native availability deterministically. Prefer reusing or mirroring probeLibghosttyVt() rather than relying on local assumptions.
  2. Update default-backend expectations:
    • when native is available, default snapshot/render wait should report libghostty-vt;
    • when native is unavailable, default snapshot/render wait should report ghostty-web;
    • default screenshot and WebM should report ghostty-web in both cases.
  3. Preserve explicit backend-selection coverage:
    • --renderer ghostty-web still forces semantic operations to ghostty-web;
    • --renderer libghostty-vt still selects native semantic operations and exercises screenshot fallback behavior where already covered.
  4. Keep cross-backend screen-hash tests as a release gate. If a fixture diverges under the new default, fix the renderer parity issue or explicitly document why that fixture cannot be defaulted.
  5. Update e2e expected inspect/rendererRuntime.backend values only where the default changed. Avoid broad snapshot churn.

Quality gate after Phase 4:

npm run test:integration -- \
  test/integration/backend-selection.test.ts \
  test/integration/host-renderer-rpc.test.ts \
  test/integration/cross-backend-screen-hash.test.ts \
  test/integration/wait-render.test.ts
npm run test:e2e -- \
  test/e2e/libghostty-vt-renderer.test.ts \
  test/e2e/hello-prompt.test.ts \
  test/e2e/renderer-errors.test.ts

Phase 5 — Update docs and release notes

Files:

  • README.md
  • docs/USAGE.md
  • docs/TROUBLESHOOTING.md
  • RELEASE.md
  • design/ARCHITECTURE.md
  • dogfood/CATALOG.md
  • possibly CHANGELOG.md if this change is prepared as a release-facing entry

Plan:

  1. Update README "How it works" to stop saying ghostty-web is the universal default. Proposed wording:
    • libghostty-vt — default for semantic actions (wait, snapshot, screen hash) when the optional native package is available; also powers the dashboard.
    • ghostty-web — default for visual actions (screenshot, WebM export); browser-backed reference visual renderer.
  2. Update Quickstart/status wording so users understand screenshots/WebM still require Playwright/Chromium, while semantic waits/snapshots can run browser-free on supported native platforms.
  3. Update docs/TROUBLESHOOTING.md line(s) that currently say ghostty-web is the reference renderer for snapshots, screenshots, and replay video. Split semantic and visual troubleshooting.
  4. Update docs/USAGE.md renderer section around screenshot/WebM to mention the semantic-vs-visual defaults and how to restore legacy behavior with --renderer ghostty-web, AGENT_TTY_RENDERER=ghostty-web, or config.
  5. Update RELEASE.md known limitations. Do not claim native pixel parity; say semantic operations may default to native libghostty-vt, while visual artifacts remain ghostty-web reference artifacts.
  6. Update design/ARCHITECTURE.md top-level decisions/current shipped status so it no longer claims ghostty-web is the only default renderer. Keep the tiered truth model: event log truth, semantic native truth, reference visual truth.
  7. Check public skill docs under skills/agent-tty/. Current search found no renderer-specific guidance, but if any examples are updated, keep public examples using agent-tty ... rather than repo-local npx tsx commands.

Quality gate after Phase 5:

npm run format:check
npm run lint
rg -n "ghostty-web.*default|default.*ghostty-web|reference renderer for snapshots" README.md docs RELEASE.md design skills dogfood -S

Dogfooding and reviewer proof bundle

Create a new proof bundle under dogfood/20260616-default-semantic-renderer/ after implementation. The bundle should be self-contained and reviewer-friendly, modeled after dogfood/20260424-libghostty-vt-renderer/.

Bundle contents

  • README.md — what changed, what the bundle proves, exact reviewer checks.
  • commands.sh — reproducible script with an isolated absolute AGENT_TTY_HOME from mktemp -d.
  • environment.txt — Node/npm versions, OS, git HEAD, version --json, doctor --json, native-addon probe info.
  • default-wait.json — default semantic wait envelope.
  • default-snapshot.json — default semantic snapshot envelope.
  • default-screenshot.json — default visual screenshot envelope.
  • default-webm.json — default visual WebM export envelope.
  • explicit-ghostty-web-snapshot.json — legacy override proof.
  • explicit-libghostty-vt-screenshot.json — explicit native screenshot-fallback proof, if native is available.
  • explicit-libghostty-vt-webm.json — proof that an explicit native WebM request is accepted but still records ghostty-web as the actual producer.
  • inspect.json — artifact manifest/runtime summary after captures.
  • screenshots/default-screenshot.png — copied screenshot artifact.
  • videos/default-webm.webm — copied WebM artifact.
  • recordings/default.cast — asciicast export, optional but useful baseline.

Dogfood script flow

  1. Build or use repo-local CLI:
    npm run build
    If implementation-time iteration does not build first, use npx tsx src/cli/main.ts inside the script and document that choice.
  2. Create isolated Home:
    export AGENT_TTY_HOME="$(mktemp -d -t agent-tty-default-renderer.XXXXXX)"
  3. Capture environment and diagnostics:
    npx tsx src/cli/main.ts version --json > version.json
    npx tsx src/cli/main.ts doctor --json > doctor.json
  4. Start the hello-prompt fixture or a tiny shell fixture in a new session.
  5. Run default semantic operations without --renderer:
    npx tsx src/cli/main.ts wait "$SID" --text "..." --json > default-wait.json
    npx tsx src/cli/main.ts snapshot "$SID" --json > default-snapshot.json
  6. Run default visual operations without --renderer:
    npx tsx src/cli/main.ts screenshot "$SID" --json > default-screenshot.json
    npx tsx src/cli/main.ts record export "$SID" --format webm --timing accelerated --json > default-webm.json
  7. Copy the generated PNG and WebM artifacts into screenshots/ and videos/.
  8. Run explicit override checks:
    npx tsx src/cli/main.ts --renderer ghostty-web snapshot "$SID" --json > explicit-ghostty-web-snapshot.json
    # If native is available:
    npx tsx src/cli/main.ts --renderer libghostty-vt screenshot "$SID" --json > explicit-libghostty-vt-screenshot.json
    npx tsx src/cli/main.ts --renderer libghostty-vt record export "$SID" --format webm --timing accelerated --json > explicit-libghostty-vt-webm.json
  9. Capture inspect --json, destroy the session, and validate all JSON envelopes with jq.
  10. Record reviewer-visible evidence:
  • screenshot file dimensions and checksum using file/sha256sum;
  • WebM file metadata using file or ffprobe if available;
  • optional terminal recording/asciicast for the flow.

Dogfood acceptance checks

  • With native available, default-snapshot.json reports rendererBackend: "libghostty-vt".
  • Native-unavailable automatic fallback is required test coverage via mocked probe/unit or integration tests. It is optional in the dogfood bundle unless there is a clean, reproducible mechanism such as a packed install with npm install --omit=optional; do not rely on brittle ad hoc deletion of files from node_modules.
  • If an optional native-unavailable dogfood lane is included, default semantic commands still succeed and report rendererBackend: "ghostty-web".
  • default-screenshot.json reports rendererBackend: "ghostty-web".
  • default-webm.json reports metadata.rendererBackend: "ghostty-web".
  • Explicit --renderer ghostty-web semantic operation reports ghostty-web.
  • Explicit --renderer libghostty-vt screenshot request either succeeds through fallback with honest ghostty-web producer metadata or fails with the existing actionable native-dependency message if native is unavailable.
  • Explicit --renderer libghostty-vt record export --format webm succeeds when visual dependencies are available and reports metadata.rendererBackend: "ghostty-web" because WebM is always browser-produced.
  • The proof bundle includes both PNG screenshots and WebM video recordings so reviewers can verify the dogfood steps visually.

Final validation gates

Run the narrowest checks after each phase, then run the full gate before claiming implementation success:

npm run format:check
npm run lint
npm run typecheck
npm run test
npm run build
npm run smoke:install -- --skip-build

Preferred final repo gate if mise is available:

mise run ci

If mise is unavailable, use:

npm run verify

Manual CLI checks with isolated Home:

TMP_HOME="$(mktemp -d)"
AGENT_TTY_HOME="$TMP_HOME" npx tsx src/cli/main.ts doctor --json
AGENT_TTY_HOME="$TMP_HOME" npx tsx src/cli/main.ts version --json

Acceptance criteria

  • No public JSON envelope keys are removed or renamed.
  • RendererNameSchema still accepts exactly ghostty-web and libghostty-vt unless a separate reviewed decision adds auto later.
  • Default semantic operations use libghostty-vt on supported installs and gracefully fall back to ghostty-web when native support is absent.
  • Default visual operations continue to produce PNG/WebM with ghostty-web and do not boot libghostty-vt; explicit libghostty-vt screenshot requests may boot native plus fallback, while explicit libghostty-vt WebM requests still normalize to a ghostty-web producer.
  • Explicit renderer selection by flag/env/config keeps precedence over automatic defaults.
  • Artifact metadata records the actual producing backend.
  • Dashboard behavior remains unchanged: it requires libghostty-vt and fails fast with the existing actionable message when unavailable.
  • Tests cover native-available and native-unavailable default resolution deterministically, including memoized probe behavior and non-fatal probe failures.
  • doctor --json/capability discovery is updated or explicitly documented as deferred; preferred acceptance is that snapshot/render-wait capabilities require at least one usable semantic renderer.
  • CLI help, docs, and release/architecture references clearly explain semantic vs visual defaults and how to restore legacy ghostty-web behavior.
  • Dogfood proof bundle includes JSON envelopes, PNG screenshot(s), WebM recording(s), and clear reviewer commands.

Risks and mitigations

Risk Mitigation
Optional native dependency makes defaults platform-dependent. Probe native availability only for automatic semantic defaults; fall back to ghostty-web when usable; surface clear capability/doctor failures when neither semantic path is usable; keep explicit ghostty-web override documented.
Adding a visual default field causes fixture churn. Preserve rendererDefault as the semantic field name and add only rendererVisualDefault; update context fixtures surgically.
Users misunderstand why screenshot metadata says ghostty-web after semantic defaults changed. Document that visual artifacts remain browser-backed; dogfood bundle proves metadata is honest.
resolveCommandContext probe adds overhead to non-render commands. Keep probe limited to the no-configured-renderer case, memoize it per process, and if timing is still noticeable during implementation, move probing into a lazy semantic resolver used only by render commands.
Capability discovery becomes more complex because wait includes both render and non-render modes. Prefer honest render-capability reporting for the coarse wait capability; if deferred, document that deferral explicitly and keep tests aligned.
Cross-backend snapshot parity regression appears under broader default use. Keep cross-backend screen-hash integration tests and libghostty e2e tests as required gates before docs/dogfood signoff.
Public docs still describe ghostty-web as universal default. Add docs grep gate and update README, USAGE, TROUBLESHOOTING, RELEASE, and architecture docs together.

Advisor review status

Approved after advisor round 2 review. Round 1 requested changes around capability reporting, CLI help, DEFAULT_RENDERER_NAME semantics, memoized/non-fatal native probing, host inheritance tests, expanded test inventory, explicit visual override costs, and realistic native-unavailable dogfood. Round 2 requested a correction that explicit libghostty-vt WebM requests are accepted but still produced by ghostty-web, plus best-effort/browser requirements for semantic fallback and quick/full capability behavior. All requested changes have been incorporated, and the advisor reported no remaining blockers.


Generated with mux • Model: openai:gpt-5.5 • Thinking: xhigh

@ThomasK33 ThomasK33 added this pull request to the merge queue Jun 16, 2026
Merged via the queue into main with commit c11e2e2 Jun 16, 2026
21 of 23 checks passed
@ThomasK33 ThomasK33 deleted the vt-impl-5ds6 branch June 16, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant