diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,34 @@
+Copyright (c) 2023 Torsten Schmits
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+  disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
+  disclaimer in the documentation and/or other materials provided with the distribution.
+
+Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those
+receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except
+for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell,
+import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired
+or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by:
+
+  (a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of
+  contributors, in source or binary form) alone; or
+  (b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such
+  copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to
+  be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution.
+
+Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this
+license, whether expressly, by implication, estoppel or otherwise.
+
+DISCLAIMER
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,1 @@
+# Unreleased
diff --git a/lib/Zeugma.hs b/lib/Zeugma.hs
new file mode 100644
--- /dev/null
+++ b/lib/Zeugma.hs
@@ -0,0 +1,50 @@
+{-# language NoImplicitPrelude #-}
+
+-- | Utilities for Incipit programs using hedgehog.
+module Zeugma (
+  -- * Test runners
+  runTest,
+  runTestDebug,
+  runTestTrace,
+  runTestLevel,
+  runTestFrozen,
+  runTestFrozenDebug,
+  runTestFrozenTrace,
+  runTestFrozenLevel,
+  testTime,
+
+  -- * Resumable to TestError conversion
+  stopTest,
+  resumeTest,
+
+  -- * Reexports of ubiquitous names
+  unitTest,
+  unitTestTimes,
+  defaultMain,
+  testGroup,
+  TestTree,
+  Failure,
+  TestStack,
+  TestError (TestError),
+  -- TODO not released yet
+  -- testError,
+) where
+
+import Hedgehog.Internal.Property (Failure)
+import Polysemy.Test (unitTest, unitTestTimes)
+import Polysemy.Test.Data.TestError (TestError (TestError))
+import Test.Tasty (TestTree, defaultMain, testGroup)
+
+import Zeugma.Run (
+  TestStack,
+  runTest,
+  runTestDebug,
+  runTestFrozen,
+  runTestFrozenDebug,
+  runTestFrozenLevel,
+  runTestFrozenTrace,
+  runTestLevel,
+  runTestTrace,
+  testTime,
+  )
+import Zeugma.TestError (resumeTest, stopTest)
diff --git a/lib/Zeugma/Run.hs b/lib/Zeugma/Run.hs
new file mode 100644
--- /dev/null
+++ b/lib/Zeugma/Run.hs
@@ -0,0 +1,138 @@
+{-# language NoImplicitPrelude #-}
+{-# options_haddock prune #-}
+
+-- | Test runners for polysemy-conc programs using hedgehog.
+module Zeugma.Run where
+
+import qualified Chronos
+import Chronos (datetimeToTime)
+import Conc (
+  Critical,
+  Gates,
+  interpretCritical,
+  interpretGates,
+  interpretInterrupt,
+  interpretMaskFinal,
+  interpretRace,
+  interpretUninterruptibleMaskFinal,
+  )
+import Hedgehog (TestT)
+import Hedgehog.Internal.Property (Failure)
+import Incipit
+import Log (Severity (Crit, Debug, Trace), interpretLogStderrLevelConc)
+import Polysemy.Chronos (ChronosTime, interpretTimeChronos, interpretTimeChronosConstant)
+import Polysemy.Test (Hedgehog, Test, TestError (TestError), runTestAuto)
+import Time (mkDatetime)
+
+type ConcTestStack' =
+  [
+    Log,
+    Interrupt,
+    Critical,
+    Gates,
+    Mask,
+    UninterruptibleMask,
+    Race,
+    Async,
+    Stop Text,
+    Error Text
+  ]
+
+type ConcTestStack =
+  ChronosTime : ConcTestStack'
+
+-- | The entirety of the effects handled by this module's interpreters.
+type TestStack =
+  ConcTestStack ++ [
+    Test,
+    Fail,
+    Error TestError,
+    Hedgehog IO,
+    Error Failure,
+    Embed IO,
+    Resource,
+    Final IO
+  ]
+
+interpretTest' ::
+  Members [Error TestError, Resource, Embed IO, Final IO] r =>
+  Severity ->
+  InterpretersFor ConcTestStack' r
+interpretTest' level =
+  mapError TestError .
+  stopToError .
+  asyncToIOFinal .
+  interpretRace .
+  interpretUninterruptibleMaskFinal .
+  interpretMaskFinal .
+  interpretGates .
+  interpretCritical .
+  interpretInterrupt .
+  interpretLogStderrLevelConc (Just level)
+
+interpretTest ::
+  Members [Error TestError, Resource, Embed IO, Final IO] r =>
+  Severity ->
+  InterpretersFor ConcTestStack r
+interpretTest level =
+  interpretTest' level .
+  interpretTimeChronos
+
+-- | The time at which the combinators ending in @Frozen@ run the 'ChronosTime' effect.
+testTime :: Chronos.Time
+testTime =
+  datetimeToTime (mkDatetime 2030 5 23 12 0 0)
+
+interpretTestFrozen ::
+  Members [Error TestError, Resource, Embed IO, Final IO] r =>
+  Severity ->
+  InterpretersFor ConcTestStack r
+interpretTestFrozen level =
+  interpretTest' level .
+  interpretTimeChronosConstant testTime
+
+-- | Run the test stack as a 'TestT' with the specified log level.
+runTestLevel ::
+  Severity ->
+  Sem TestStack a ->
+  TestT IO a
+runTestLevel level =
+  runTestAuto . interpretTest level
+
+-- | Run the test stack as a 'TestT' with the specified log level, with 'ChronosTime' frozen at 'testTime'.
+runTestFrozenLevel ::
+  Severity ->
+  Sem TestStack a ->
+  TestT IO a
+runTestFrozenLevel level =
+  runTestAuto . interpretTestFrozen level
+
+-- | Run the test stack as a 'TestT' with a log level of 'Trace'.
+runTestTrace :: Sem TestStack a -> TestT IO a
+runTestTrace =
+  runTestLevel Trace
+
+-- | Run the test stack as a 'TestT' with a log level of 'Debug'.
+runTestDebug :: Sem TestStack a -> TestT IO a
+runTestDebug =
+  runTestLevel Debug
+
+-- | Run the test stack as a 'TestT' with a log level of 'Crit'.
+runTest :: Sem TestStack a -> TestT IO a
+runTest =
+  runTestLevel Crit
+
+-- | Run the test stack as a 'TestT' with a log level of 'Trace' and 'ChronosTime' frozen at 'testTime'.
+runTestFrozenTrace :: Sem TestStack a -> TestT IO a
+runTestFrozenTrace =
+  runTestFrozenLevel Trace
+
+-- | Run the test stack as a 'TestT' with a log level of 'Debug' and 'ChronosTime' frozen at 'testTime'.
+runTestFrozenDebug :: Sem TestStack a -> TestT IO a
+runTestFrozenDebug =
+  runTestFrozenLevel Debug
+
+-- | Run the test stack as a 'TestT' with a log level of 'Crit' and 'ChronosTime' frozen at 'testTime'.
+runTestFrozen :: Sem TestStack a -> TestT IO a
+runTestFrozen =
+  runTestFrozenLevel Crit
diff --git a/lib/Zeugma/TestError.hs b/lib/Zeugma/TestError.hs
new file mode 100644
--- /dev/null
+++ b/lib/Zeugma/TestError.hs
@@ -0,0 +1,28 @@
+{-# language NoImplicitPrelude #-}
+{-# options_haddock prune #-}
+
+-- | Combinators related to 'TestError'.
+module Zeugma.TestError where
+
+import Incipit
+import Polysemy.Test (TestError (TestError))
+
+-- | Interpret 'Stop' by throwing a 'TestError', with the call site's stack.
+stopTest ::
+  ∀ err r .
+  Show err =>
+  HasCallStack =>
+  Member (Error TestError) r =>
+  InterpreterFor (Stop err) r
+stopTest ma =
+  withFrozenCallStack (stopToErrorWith (TestError . show) ma)
+
+-- | Interpret an effect into 'Resumable' by throwing a 'TestError' for 'Stop's, with the call site's stack.
+resumeTest ::
+  ∀ err eff r .
+  Show err =>
+  HasCallStack =>
+  Members [eff !! err, Error TestError] r =>
+  InterpreterFor eff r
+resumeTest ma =
+  withFrozenCallStack (resumeHoistError @err (TestError . show) ma)
diff --git a/readme.md b/readme.md
new file mode 100644
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,9 @@
+# About
+
+This Haskell library provides test runners for [incipit](https://hackage.haskell.org/package/incipit)-based projects,
+building on [polysemy-test](https://hackage.haskell.org/package/polysemy-test), which uses
+[hedgehog](https://hackage.haskell.org/package/hedgehog) and [tasty](https://hackage.haskell.org/package/tasty).
+
+It is mere convenience to avoid the necessity of manually interpreting the commonly used effects from the bundle of
+libraries that constitute Incipit.
+Consult [Hackage](https://hackage.haskell.org/package/zeugma/docs/Zeugma.html) for the list of runners.
diff --git a/zeugma.cabal b/zeugma.cabal
new file mode 100644
--- /dev/null
+++ b/zeugma.cabal
@@ -0,0 +1,115 @@
+cabal-version: 2.2
+
+-- This file has been generated from package.yaml by hpack version 0.35.0.
+--
+-- see: https://github.com/sol/hpack
+
+name:           zeugma
+version:        0.7.0.0
+synopsis:       Polysemy effects for testing
+description:    See https://hackage.haskell.org/package/zeugma/docs/Zeugma.html
+category:       Prelude
+homepage:       https://git.tryp.io/tek/incipit
+bug-reports:    https://github.com/tek/incipit/issues
+author:         Torsten Schmits
+maintainer:     hackage@tryp.io
+copyright:      2022 Torsten Schmits
+license:        BSD-2-Clause-Patent
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    changelog.md
+    readme.md
+
+source-repository head
+  type: git
+  location: https://git.tryp.io/tek/incipit
+
+library
+  exposed-modules:
+      Zeugma
+      Zeugma.Run
+      Zeugma.TestError
+  reexported-modules:
+      Hedgehog
+    , Polysemy.Test
+    , Test.Tasty
+    , Test.Tasty.ExpectedFailure
+    , Test.Tasty.Hedgehog
+  hs-source-dirs:
+      lib
+  default-extensions:
+      AllowAmbiguousTypes
+      ApplicativeDo
+      BangPatterns
+      BinaryLiterals
+      BlockArguments
+      ConstraintKinds
+      DataKinds
+      DefaultSignatures
+      DeriveAnyClass
+      DeriveDataTypeable
+      DeriveFoldable
+      DeriveFunctor
+      DeriveGeneric
+      DeriveLift
+      DeriveTraversable
+      DerivingStrategies
+      DerivingVia
+      DisambiguateRecordFields
+      DoAndIfThenElse
+      DuplicateRecordFields
+      EmptyCase
+      EmptyDataDecls
+      ExistentialQuantification
+      FlexibleContexts
+      FlexibleInstances
+      FunctionalDependencies
+      GADTs
+      GeneralizedNewtypeDeriving
+      InstanceSigs
+      KindSignatures
+      LambdaCase
+      LiberalTypeSynonyms
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      OverloadedLabels
+      OverloadedLists
+      OverloadedStrings
+      PackageImports
+      PartialTypeSignatures
+      PatternGuards
+      PatternSynonyms
+      PolyKinds
+      QuantifiedConstraints
+      QuasiQuotes
+      RankNTypes
+      RecordWildCards
+      RecursiveDo
+      RoleAnnotations
+      ScopedTypeVariables
+      StandaloneDeriving
+      TemplateHaskell
+      TupleSections
+      TypeApplications
+      TypeFamilies
+      TypeFamilyDependencies
+      TypeOperators
+      TypeSynonymInstances
+      UndecidableInstances
+      UnicodeSyntax
+      ViewPatterns
+  ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Widentities -Wunused-packages
+  build-depends:
+      chronos
+    , hedgehog
+    , incipit ==0.7.0.0
+    , polysemy ==1.9.*
+    , polysemy-chronos ==0.6.*
+    , polysemy-test ==0.7.*
+    , tasty ==1.4.*
+    , tasty-expected-failure ==0.12.*
+    , tasty-hedgehog >=1.3 && <1.5
+    , unix
+  default-language: Haskell2010
