07e29ef9

Add autodocodec-http-api-data for form-urlencoded data

Derives `Web.FormUrlEncoded.ToForm` and `FromForm` from a `HasObjectCodec`
instance, so a type with a codec can be posted as
`application/x-www-form-urlencoded` without a second, hand-written description
of its fields.

## Why

The motivating case is an API endpoint that a shell should be able to hit with
curl alone:

```
curl --netrc --silent --show-error --fail-with-body \
  --data-urlencode "clone-url=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git" \
  --data-urlencode "message=$COMMIT_MESSAGE" \
  https://api.nix-ci.com/suite
```

The point of the form is that `--data-urlencode` escapes each value, so a commit
message containing a quote or a newline needs no JSON encoder in the shell.

The type already had a `HasObjectCodec` instance naming all its fields, but with
no autodocodec support the `FromForm` instance had to be written by hand, so the
field names were spelled twice and a test had to assert the two lists agreed.
That test is exactly the check this package makes unnecessary.

`http-api-data`'s own generic derivation is not a substitute: it derives names
from Haskell record fields via a label modifier, so it would be a third
independent spelling of the same names rather than a shared one.

## What

`autodocodec-servant-multipart`'s interpreter retargeted at
`Web.FormUrlEncoded.Form`, which is likewise a flat map from text keys to text
values. A `Form`'s `HashMap Text [Text]` models repeated keys natively, so
`ArrayOfCodec` fits it better than it fits multipart's flat `[Input]`.

Three places where copying multipart verbatim would have been wrong:

- **`unionForm`, not `<>`.** `Form` newtype-derives `Semigroup` from `HashMap`,
  whose union is left-biased, so `<>` would silently drop the second form's
  values under a shared key — where multipart's `mappendMultipartData`
  concatenates.
- **Booleans encode lower case.** Multipart's encoder emits Haskell spelling;
  `toUrlPiece @Bool` is `T.toLower . show`, so `"True"` would round-trip through
  our own decoder but read as unconventional to every other form consumer. The
  decoder still accepts either spelling.
- **A field is absent exactly when its key is absent**, which is lossless and
  matches `http-api-data`'s own `lookupMaybe`. `dropEmptyFormValues :: Form ->
  Form` is exported separately for callers who need a shell's unset variable
  (`--data-urlencode "x=$UNSET"` sends `x=`) to read as absence, at the cost of
  making a genuinely empty value inexpressible. That is a caller's choice rather
  than a policy threaded through the interpreter.

Nested values keep multipart's behaviour — JSON-encoded into the text slot —
documented in the module header as something no other form parser will
understand.

## Tests

`autodocodec-api-usage/test/Autodocodec/FormUrlEncodedSpec.hs` mirrors
`MultipartSpec`: "matches the encoding", "matches the decoding" and a
round trip, over `Example`, `Via`, `LegacyValue`, `LegacyObject`, `These`,
`Expression`, `ListsExample` and `Overlap`. Hand-written `ToForm`/`FromForm
Example` in `Usage.hs` give the first two something independent to compare
against, as the multipart pair do.

Two additions beyond that mirror:

- `dropEmptyFormValues` asserted on both sides of the policy: an empty value
  stays a value without it, and reads as an absent field with it.
- A round trip through `urlEncodeFormStable`/`urlDecodeForm`, so the actual
  percent-escaped wire bytes are covered. That escaping is the reason the
  feature exists.

**No `xdescribe` was needed.** `MultipartSpec` skips `Example` with "does not
hold."; here the whole corpus round-trips, `Example` included. I ran 480,000
examples per property rather than trusting the default 100 before concluding
that. Grouping repeated keys is what makes the difference.

## Checks

- `nix flake check` passes. The new package builds under `-Werror` against
  nixpkgs 26.05, 25.11 and 25.05 plus horizon-advance, so against four
  `http-api-data` versions.
- Full `autodocodec-api-usage` suite: 77,482 examples, 0 failures.
- `pre-commit run -a` clean.

`stack build --pedantic` cannot pass in this tree, and not because of this
change: `autodocodec-swagger2` has an unused `aeson` dependency that
`-Wunused-packages` rejects under `-Werror`. I confirmed that on a clean tree
before working around it. It does not affect `nix flake check`, whose override
does not enable that warning, but it does mean the cheapest feedback loop is
unusable until a separate one-line fix to `autodocodec-swagger2/package.yaml`.

## Not in this PR

`cabal.project` needs no change (it globs `*/*.cabal`); `stack.yaml` does, since
it lists packages explicitly. Replacing the first hand-written caller lives in
the nix-ci tree, on branch `openapi-spec` (NorfairKing/nix-ci#463).

Suite timing

Time to Start Worker time Duration Time to finish Idle
Config 12s 1s 1s 13s 12s
Eval 14s 13s 13s 27s 0s
Build 23s 1m36s 32m14s 32m38s 30m33s
Suite 12s 1m51s 32m26s 32m38s 30m46s

Timeline

0s20s31m10s31m20s31m30s31m40s31m50s32m32m10s32m20s32m30s