Route iw feedback and iw issue create through ForgeConfigResolver instead of inlining forge resolution #456

Open
opened 2026-08-26 13:05:06 +00:00 by mph · 4 comments
Owner

What

The same "read FORGEJO_API_TOKEN, wrap it in ApiToken" pipeline lives in four places:

  • core/commands/Feedback.scala:22-25 (added by IW-426)
  • core/commands/Issue.scala:117-123
  • core/commands/ForgeConfigResolver.scala:44-46 (the same .get(...).flatMap(ApiToken.apply) inline)
  • core/model/ForgeConfig.scala:27-28 (a fourth copy of the message text)

The duplication is wider than the token. Issue.createIssue (core/commands/Issue.scala:208-270)
inlines its own per-forge resolution — base URL, token and forge dispatch — for all five tracker
types, and never calls ForgeConfigResolver.resolve, which exists to do exactly that job and
already encodes the GitHub/GitLab-vs-Forgejo asymmetry via ForgeConfig.empty.

A resolver that no caller routes through is not a seam; when a command inlines the resolution the
resolver already performs, the duplication grows on every new command rather than being absorbed.

Proposed change

Route iw feedback and iw issue create through ForgeConfigResolver.resolve for the whole forge
resolution (base URL, token, forge dispatch), not just the token, and give ApiToken an
EnvVars-taking lookup so the resolver itself stops inlining the pipeline.

ApiToken.fromEnv (core/model/ApiToken.scala:35-36) is sys.env.get(envVar).flatMap(apply) — it
reads the real process environment, so any caller using it makes its own tests depend on the
developer's shell. IW-426/phase-01-context.md rules it out of the feedback command for exactly
this reason. The EnvVars-taking lookup is what removes that obstacle.

Constraints the refactoring must preserve

  1. Feedback's target stays hardcoded to iw-cli on Forgejo and must never follow the local project's
    tracker config. Recorded as deliberate at core/model/Constants.scala:77, test/feedback.bats:5-6,
    and IWLE-132/refactor-phase-03-R1.md:21.
  2. ForgeConfigResolver currently resolves from ProjectConfiguration, so feedback needs a path
    that supplies a fixed target rather than reading project config.

Found during IW-426 (Phase 1 code review, finding W4).

## What The same "read `FORGEJO_API_TOKEN`, wrap it in `ApiToken`" pipeline lives in four places: - `core/commands/Feedback.scala:22-25` (added by IW-426) - `core/commands/Issue.scala:117-123` - `core/commands/ForgeConfigResolver.scala:44-46` (the same `.get(...).flatMap(ApiToken.apply)` inline) - `core/model/ForgeConfig.scala:27-28` (a fourth copy of the message text) The duplication is wider than the token. `Issue.createIssue` (`core/commands/Issue.scala:208-270`) inlines its own per-forge resolution — base URL, token and forge dispatch — for all five tracker types, and never calls `ForgeConfigResolver.resolve`, which exists to do exactly that job and already encodes the GitHub/GitLab-vs-Forgejo asymmetry via `ForgeConfig.empty`. A resolver that no caller routes through is not a seam; when a command inlines the resolution the resolver already performs, the duplication grows on every new command rather than being absorbed. ## Proposed change Route `iw feedback` and `iw issue create` through `ForgeConfigResolver.resolve` for the whole forge resolution (base URL, token, forge dispatch), not just the token, and give `ApiToken` an `EnvVars`-taking lookup so the resolver itself stops inlining the pipeline. `ApiToken.fromEnv` (`core/model/ApiToken.scala:35-36`) is `sys.env.get(envVar).flatMap(apply)` — it reads the real process environment, so any caller using it makes its own tests depend on the developer's shell. `IW-426/phase-01-context.md` rules it out of the feedback command for exactly this reason. The `EnvVars`-taking lookup is what removes that obstacle. ## Constraints the refactoring must preserve 1. Feedback's target stays hardcoded to iw-cli on Forgejo and must never follow the local project's tracker config. Recorded as deliberate at `core/model/Constants.scala:77`, `test/feedback.bats:5-6`, and `IWLE-132/refactor-phase-03-R1.md:21`. 2. `ForgeConfigResolver` currently resolves from `ProjectConfiguration`, so feedback needs a path that supplies a fixed target rather than reading project config. Found during IW-426 (Phase 1 code review, finding W4).
Member

Seen again in IW-610: Issue.createIssue repeats the token, repository and base-URL resolution that resolveTrackerAccess (core/commands/Issue.scala) now holds for fetches — a second inline copy inside the same command.

Seen again in IW-610: Issue.createIssue repeats the token, repository and base-URL resolution that resolveTrackerAccess (core/commands/Issue.scala) now holds for fetches — a second inline copy inside the same command.
Member

Intake: agent-ready
Workflow: waterfall
Reason: all gates passed
Analysis: project-management/issues/IW-456/analysis.md on branch IW-456

Intake: agent-ready Workflow: waterfall Reason: all gates passed Analysis: project-management/issues/IW-456/analysis.md on branch IW-456
Member

Implement: needs-human
Reason: batch-implement exit 1 — the kanon plugin command did not compile against the iw-cli core of this branch (which is the current main), so no phase ran and no batch-implement.log was written. ForgeCatchUp.scala expects Either[String, …] and the core now returns Either[TrackerError, …]. The cause is in the plugin, not in this issue.

[error] /home/mph/.local/share/iw/plugins/kanon/lib/batch/ForgeCatchUp.scala:143:7
[error] Found:    Either[iw.core.model.TrackerError, Option[batch.ForgeState]]
[error] Required: Either[String, Option[batch.ForgeState]]
Compilation failed
Implement: needs-human Reason: batch-implement exit 1 — the kanon plugin command did not compile against the iw-cli core of this branch (which is the current main), so no phase ran and no batch-implement.log was written. `ForgeCatchUp.scala` expects `Either[String, …]` and the core now returns `Either[TrackerError, …]`. The cause is in the plugin, not in this issue. ``` [error] /home/mph/.local/share/iw/plugins/kanon/lib/batch/ForgeCatchUp.scala:143:7 [error] Found: Either[iw.core.model.TrackerError, Option[batch.ForgeState]] [error] Required: Either[String, Option[batch.ForgeState]] Compilation failed ```
Member

Release: the implement loop failed this issue for a reason outside it. The kanon plugin did not compile against iw-cli main, because the tracker port now returns TrackerError (iw-cli #778). The plugin fix is dev-docs #484, and iw-cli 0.22.1 carries the core change. The issue goes back to the implement loop.

Release: the implement loop failed this issue for a reason outside it. The kanon plugin did not compile against iw-cli main, because the tracker port now returns TrackerError (iw-cli #778). The plugin fix is dev-docs #484, and iw-cli 0.22.1 carries the core change. The issue goes back to the implement loop.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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
iterative-works/iw-cli#456
No description provided.