InkkOops
Runtime and CLI
This page explains how InkkOops is started, how startup work differs from attach-mode work, how the companion CLI maps onto the runtime service, and which operational guarantees you can rely on.
Entry points
- App command line. Start the host app with InkkOops-specific flags such as
--inkkoops-scriptor--inkkoops-recording. - Companion CLI. Use
InkkOops.Clito list scripts, launch startup runs, attach to a running app, or launch recording mode.
Both paths end up driving the same in-process runtime service.
App command line flags
--inkkoops-script <name>: run a startup script.--inkkoops-recording <path>: replay a recording at startup.--inkkoops-record: record user interaction and write the recording bundle on normal shutdown.--inkkoops-artifacts <path>: override the run artifact root.--inkkoops-record-root <path>: override the recording root.--inkkoops-pipe <name>: override the attach-mode pipe name.--inkkoops-disable-retained: disable retained rendering for renderer diagnostics.--inkkoops-disable-dirty: disable dirty-region rendering for renderer diagnostics.
CLI commands
dotnet run --project InkkOops.Cli/InkkOops.Cli.csproj -- list
dotnet run --project InkkOops.Cli/InkkOops.Cli.csproj -- run --script "sidebar-button-richtextbox" --launch
dotnet run --project InkkOops.Cli/InkkOops.Cli.csproj -- run --script "sidebar-button-richtextbox" --attach
dotnet run --project InkkOops.Cli/InkkOops.Cli.csproj -- record --launch
The CLI resolves its launch target through a resolver abstraction. In this repository, the default resolver falls back to InkkSlinger.csproj, but you can also provide --project <path> explicitly.
Startup runs vs attach runs
Startup script run
The app launches, waits for initial layout, runs the configured startup script, writes artifacts, and exits automatically.
Startup recording replay
The app launches, converts the recording into a replay script, runs it, writes artifacts, and exits automatically.
Attach mode
The app is already running. The request arrives over the named pipe and executes without forcing the app to exit afterward.
Recording mode
The app records human interaction. The recording is written when the app closes normally.
Serialization and concurrency
Attach mode is serialized. The runtime accepts only one queued or active request at a time. If a script is already queued or running, the attach response is Busy.
Automatic exit behavior
- Startup script runs exit automatically after completion.
- Startup recording replays exit automatically after completion.
- Attach-mode requests do not exit the app.
- Recording mode writes output on normal shutdown and does not force early exit.
Examples
Startup script:
dotnet run --project InkkSlinger.csproj -- --inkkoops-script "datagrid-workbench-capture"
Startup replay with custom artifacts:
dotnet run --project InkkSlinger.csproj -- `
--inkkoops-recording ".\artifacts\inkkoops-recordings\20260328-001157740-recorded-session\recording.json" `
--inkkoops-artifacts "artifacts/my-replay-run"
Attach mode with explicit pipe:
dotnet run --project InkkOops.Cli/InkkOops.Cli.csproj -- run `
--script "buttons-resize-hover-repro" `
--attach `
--pipe "inkkoops-dev" `
--timeout 45000 `
--artifacts "artifacts/inkkoops-dev"
Operational guidance
- Use startup runs for cold-start reproducibility.
- Use attach mode when you want to keep the app open and iterate quickly.
- Use dedicated artifact roots when comparing multiple runs.
- Use the renderer diagnostic switches only when isolating renderer-specific bugs.
xUnit example
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using Xunit;
public sealed class ControlsCatalogRuntimeTests
{
[Fact]
public void StartupScript_Run_Writes_Artifacts()
{
var repoRoot = Environment.CurrentDirectory;
var artifactRoot = Path.Combine(repoRoot, "artifacts", "inkkoops-xunit");
var startInfo = new ProcessStartInfo
{
FileName = "dotnet",
WorkingDirectory = repoRoot,
UseShellExecute = false
};
startInfo.ArgumentList.Add("run");
startInfo.ArgumentList.Add("--project");
startInfo.ArgumentList.Add("InkkSlinger.csproj");
startInfo.ArgumentList.Add("--");
startInfo.ArgumentList.Add("--inkkoops-script");
startInfo.ArgumentList.Add("sidebar-button-richtextbox");
startInfo.ArgumentList.Add("--inkkoops-artifacts");
startInfo.ArgumentList.Add(artifactRoot);
using var process = Process.Start(startInfo)!;
Assert.True(process.WaitForExit(120_000));
Assert.Equal(0, process.ExitCode);
var runDirectory = Directory.GetDirectories(artifactRoot).Single();
using var document = JsonDocument.Parse(File.ReadAllText(Path.Combine(runDirectory, "result.json")));
Assert.Equal("Completed", document.RootElement.GetProperty("status").GetString());
}
}