600f80d0

Fail on empty scenario directories, and scenarios that are directories

An empty `scenarioDir` suite now fails, and scenarios can be directories.

### Fail on an empty scenario directory

An empty scenario directory defined no tests at all, so a suite whose scenario files went missing, left out of `extra-source-files` or behind a mistyped path, reported success. `scenarioDir` and `scenarioDirRecur` now define a single failing test when they find no files. An absent directory takes the same path, because `forgivingAbsence` already turns absence into an empty list.

This is the reasoning that already makes an empty test suite fail, applied one level down.

### `scenarioDirOfDirs`

A scenario often consists of more than one file: an input and the output it must produce, a config and a golden rendering of it. The two existing definers can only hand a single file to the test definition, so such a suite had to reconstruct the grouping by hand.

```haskell
scenarioDirOfDirs "test_resources/same" $ \fp ->
  it "contains two files with the same contents" $ do
    a <- readFile (fp </> "a")
    b <- readFile (fp </> "b")
    a `shouldBe` b
```

Each subdirectory is one scenario, whatever it contains. Files in the given directory itself are ignored, and so is any nesting below the subdirectories. There is no recursive variant, because a scenario directory has no obvious depth at which to stop.

The three definers now share a helper parameterised over how it lists scenarios, so the empty case behaves identically for all of them.

### A separable third commit

`Test.Syd.TimingSpec.long diff timing is bounded` failed in two of three `nix flake check` runs here, unrelated to the above; the first failure predates the `scenarioDirOfDirs` work. The test bounds its inner run at 120s, deliberately generously, so that it fails only if the 2s diff timeout stopped working. The default test timeout is 60s, so on a machine slow enough for that bound to matter the suite killed the test first: `Timeout!` after ~65s in the nix sandbox, while printing the two 100k-element lists. The last commit raises the timeout for that one test above the bound it asserts. Drop that commit if you would rather handle it separately.

### Testing

`test_resources/same/{numbers,words}/{a,b}` exercises the real path. Beyond that, `ScenarioSpec` builds specs with `execTestDefM`, runs them, and asserts over `computeTestSuiteStats`: the empty and absent directories produce exactly one failure and no successes, and a directory holding one subdirectory, a nested subdirectory inside it, and a loose file produces exactly one scenario. The empty-directory tests fail against the unmodified library, and the nesting test fails if the lister is swapped for `listDirRecurRel`.

`stack test sydtest-test --pedantic` passes, and `nix flake check` passes on a run that rebuilt most of the tree in parallel, which is the load that previously exposed the timing flake.

### Breaking

`scenarioDir` and `scenarioDirRecur` now fail where they previously defined nothing, so sydtest is bumped to 0.28.0.0.
forwardCompatibilitymutation-manifest-examplemutation-sydtest-mutation-examplemutation-sydtest-mutation-example-crosspre-commitreleaseshellconfiguredevShelldefaultmutation-sydtest-mutation-example-diffrelease-to-hackagex86_64-linux