feat(engine): a forge-neutral failure type and listers for Gitea servers #203

Merged
james.maes merged 2 commits from feat/202-gitea-listers into main 2026-11-11 22:21:51 +00:00
2 Commits
Author SHA1 Message Date
james.maesandClaude Fable 5.2 bce029dc16 fix(engine): review of #203: a refused address is not repeated, a list ends only on what the server says about paging, and more is bounded
An independent review found no way to send a token to another host on an
address that was typed right, and these gaps.

- An address that is refused is no longer repeated in the message: one that
  was refused for the password or the token in it put that into the text.
- 'https:/host' with one slash was taken for a host named 'https'. Text
  without a scheme is a host with at most a port; anything else is refused.
  A host with characters that have no place in a name is refused instead of
  throwing.
- The client all accounts share keeps no cookies: what one account's server
  set travelled with another account's token.
- A Link header that is not about paging (one a proxy added) ended a list
  after its first page. Only paging relations say that there is no next page.
- A connection lost while an answer is read is 'unavailable', not an
  unexpected error. A page with more items than were asked for is refused, so
  a list has an upper bound. A token with a space or a line break in it is
  refused before anything is sent, and is not repeated.
- A token that may not read organizations is refused at an organization's
  list whatever the name is: the account path is tried then too, so it still
  lists its own account.
- A redirect says that the organization or account may have been renamed;
  408 is 'retry later'; other statuses no longer claim there is no API.
- Tests for each, for the escaping of names in requests and clone addresses,
  for the time limit covering the reading of an answer, and that the client
  of production is the one built on the handler whose settings are asserted.

Refs #202

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-11 01:08:18 -06:00
james.maesandClaude Fable 5.2 0d76ea6a95 feat(engine): a forge-neutral failure type and listers for Gitea servers
The engine can list the organizations and repositories of a Gitea server (or
one that speaks its API) beside GitHub. Library only: accounts, the CLI and
the app do not use it yet (#201).

- ForgeKind, and ForgeAccessException with ForgeAccessKind: what a failed
  request to any forge means to a caller. GitHubAccessException derives from
  it and keeps its own Kind, so code written against it still compiles and
  still catches.
- GiteaServerUrl: the address of a server is HTTPS, without credentials, and
  without anything after its path. There is no setting for http.
- GiteaRepositoryLister and GiteaOrganizationLister, on HttpClient and
  source-generated JSON, with no new package. The token goes to the server
  the lister was made for and nowhere else: redirects are not followed, the
  address of a next page is never taken from an answer, and clone addresses
  are built from the configured address instead of being read from the
  server. Certificates are validated by the platform; nothing switches that
  off. Answers, lists and requests are bounded.
- Paging follows what the server says about a next page: a Gitea server caps
  the page size and leaves out what the token may not see, so a short or
  empty page does not end a list.
- A lister contract test suite that the listers of both forges pass.
- ADR 0010; threat model T16.

Closes #202

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-11 00:49:22 -06:00