Domain model, Forge interface and contract-test harness #4

Closed
opened 2026-10-01 21:55:15 -05:00 by deadstyle · 2 comments
Owner

Problem

Every other v1 ticket builds on lazyforge's own types and the Forge interface. They have to exist, and be checked against more than one forge on paper, before adapters, core or UI are written against them.

Plan

  1. internal/domain: Repo, ChangeRequest, Issue, Comment, Run, Job, Release, CIState, State, RenovateUpdate (shape informed by the Renovate parsing spike).
  2. internal/forge: the Forge interface, the optional capability interfaces, HostInfo (kind, display terms "PR"/"MR", current user), and the sentinel errors (ErrNotFound, ErrUnauthorized, ErrRateLimited, ErrUnsupported).
  3. A paper check: a table in this ticket mapping each interface method to GitHub and GitLab endpoints, with gaps called out. Adjust the interface where the mapping breaks before merging.
  4. internal/forge/forgetest: an in-memory fake Forge for core/UI tests, and the skeleton of the shared adapter contract suite (a function that takes a Forge constructor and runs behavioral tests).
  5. Update docs/architecture.md to match the code. Write an ADR if the shape differs materially from the sketch there.

Plan-review additions (#17)

  • Renovate updates are a list. ChangeRequest.Renovate becomes []RenovateUpdate (empty when not a Renovate PR, or unparseable). Each update carries its ecosystem (manager / datasource) and package identity as well as name, from, to and bump, so grouping can key on ecosystem + package + from + to. Exact fields come from #2's fixtures.
  • Typed targets for Comment and Close. These take a resource reference that says whether the number is an issue or a change request (GitLab routes them differently), instead of a bare RepoRef + int.
  • Merge validates the head. MergeOpts carries the head SHA the user confirmed; the adapter passes it to the forge (Gitea head_commit_id) so a PR that changed after confirmation fails instead of merging unseen commits.
  • Capability is three things, not one. A type assertion says the adapter can. Availability also depends on the server version (one adapter covers diverging Gitea and Forgejo releases) and on the user's permission in the repo. The interface exposes availability with a human-readable disabled reason ("needs Forgejo ≥ 9", "you don't have write access") so the UI can explain a hidden or disabled key.
  • The GitHub/GitLab paper check (step 3) must cover all four of the above.

Context

  • docs/architecture.md → domain model and Forge interface sketches, API mapping table.
  • ADR 0002 (capabilities as optional interfaces, detected by type assertion).
  • go-development → Layering (domain imports nothing internal; forge is interfaces only) and Testing (fakes live in forgetest, never in production code).

Out of scope

Any adapter implementation.

## Problem Every other v1 ticket builds on lazyforge's own types and the `Forge` interface. They have to exist, and be checked against more than one forge on paper, before adapters, core or UI are written against them. ## Plan 1. `internal/domain`: `Repo`, `ChangeRequest`, `Issue`, `Comment`, `Run`, `Job`, `Release`, `CIState`, `State`, `RenovateUpdate` (shape informed by the Renovate parsing spike). 2. `internal/forge`: the `Forge` interface, the optional capability interfaces, `HostInfo` (kind, display terms "PR"/"MR", current user), and the sentinel errors (`ErrNotFound`, `ErrUnauthorized`, `ErrRateLimited`, `ErrUnsupported`). 3. A paper check: a table in this ticket mapping each interface method to GitHub and GitLab endpoints, with gaps called out. Adjust the interface where the mapping breaks before merging. 4. `internal/forge/forgetest`: an in-memory fake `Forge` for core/UI tests, and the skeleton of the shared adapter contract suite (a function that takes a `Forge` constructor and runs behavioral tests). 5. Update `docs/architecture.md` to match the code. Write an ADR if the shape differs materially from the sketch there. ### Plan-review additions (#17) - **Renovate updates are a list.** `ChangeRequest.Renovate` becomes `[]RenovateUpdate` (empty when not a Renovate PR, or unparseable). Each update carries its ecosystem (manager / datasource) and package identity as well as name, from, to and bump, so grouping can key on ecosystem + package + from + to. Exact fields come from #2's fixtures. - **Typed targets for Comment and Close.** These take a resource reference that says whether the number is an issue or a change request (GitLab routes them differently), instead of a bare `RepoRef` + `int`. - **Merge validates the head.** `MergeOpts` carries the head SHA the user confirmed; the adapter passes it to the forge (Gitea `head_commit_id`) so a PR that changed after confirmation fails instead of merging unseen commits. - **Capability is three things, not one.** A type assertion says the adapter *can*. Availability also depends on the server version (one adapter covers diverging Gitea and Forgejo releases) and on the user's permission in the repo. The interface exposes availability with a human-readable disabled reason ("needs Forgejo ≥ 9", "you don't have write access") so the UI can explain a hidden or disabled key. - The GitHub/GitLab paper check (step 3) must cover all four of the above. ## Context - `docs/architecture.md` → domain model and `Forge` interface sketches, API mapping table. - ADR 0002 (capabilities as optional interfaces, detected by type assertion). - `go-development` → Layering (domain imports nothing internal; forge is interfaces only) and Testing (fakes live in `forgetest`, never in production code). ## Out of scope Any adapter implementation.
Author
Owner

🤖 started: composer-lite. Shape decided in ADR 0006 (on feature/4-domain-forge-interface), including the GitHub/GitLab paper check.

🤖 started: composer-lite. Shape decided in ADR 0006 (on `feature/4-domain-forge-interface`), including the GitHub/GitLab paper check.
Author
Owner

🤖 Delivered locally on feature/4-domain-forge-interface (cd73e55..08ffa26): domain types, Forge interface with ItemRef/MergeOpts.HeadSHA/Gate/Can, forgetest Fake + RunContract. Review passed; mutation check killed 63/64 mutants (the survivor is equivalent). make check exit 0. MR waits on SSH :2222.

🤖 Delivered locally on `feature/4-domain-forge-interface` (cd73e55..08ffa26): domain types, Forge interface with ItemRef/MergeOpts.HeadSHA/Gate/Can, forgetest Fake + RunContract. Review passed; mutation check killed 63/64 mutants (the survivor is equivalent). `make check` exit 0. MR waits on SSH :2222.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
deadstyle/lazyforge#4
No description provided.