Replace tea with forgejo-api in os-backlog #856

Closed
opened 2026-09-30 18:14:37 +00:00 by jared · 5 comments
Owner

Problem

os-backlog runs the tea CLI for every Forgejo operation.
Ticket titles, bodies, and comments go through argv.
tea issue create prints text, so CommandRunner extracts the number and URL with a regex.
ADR-0256 replaces tea with an in-process Forgejo REST client.
ADR-0257 names that client: the forgejo-api gem is the only Forgejo client.

plugins/os-backlog/lib/backlog/tea_backend.rb:7-9   tea issue create --repo S --title T --description B [--labels a,b]   (regex on stdout)
plugins/os-backlog/lib/backlog/tea_backend.rb:38    tea comment --repo S N BODY
plugins/os-backlog/lib/backlog/issue_show.rb:27     tea issues N --repo S -o json
plugins/os-backlog/lib/backlog/inspect_command.rb:73,80   tea issues list ... (JSON, then a line-count fallback)
plugins/os-backlog/bin/wakeup-poll:15,40            tea issues list --repo S --state open --output json

tea_backend.rb has 14 tea command shapes in total: create, labels list, issues list, issue view, comment, close, comment ls, label add, label remove, assign, unassign, and edit.

Reproduce

n/a: design change from ADR-0256 and ADR-0257.

Approach

A new Backlog::ForgejoBackend keeps the public contract of TeaBackend (same methods, same argument order with repo_slug first, same return shapes) and delegates each call to ::Forgejo::Client.
Callers and GhBackend do not change.
Issues#route builds it for forgejo: trackers.
The client is an injected client: keyword.
The default is built lazily on first Forgejo call: ::Forgejo::Client.new(url: "https://forgejo.swansoncloud.com", api_key: ENV.fetch("FORGEJO_TOKEN")).
A github: or repo: tracker never needs FORGEJO_TOKEN.
IssueShow, InspectCommand, and bin/wakeup-poll read through the same backend.
TeaBackend and its tea argv builders are deleted.

Method mapping (repo = client.repo(owner, name), from the owner/name slug):

  • create(slug, title:, body:, labels:) calls repo.issues.create(title:, body:, labels: [ids]) and returns { number: issue.number, url: issue.html_url }.
  • labels_list(slug) reads repo.labels.list and returns [{ name:, description: }].
  • list(slug) reads repo.issues.list(state: "open", type: "issues") (drain the lazy page with to_a) and returns { number:, title:, labels: [names], assignee: } rows.
  • labels(slug, n) and fetch(slug, n) read repo.issues.get(n).
  • comment calls repo.comments.create(n, body:). comments maps repo.comments.list_for_issue(n) to bodies.
  • close calls repo.issues.update(n, state: "closed"). closed? reads repo.issues.get(n).
  • add_label and remove_label call repo.issues.labels(n).add(labels: [id]) and .remove(id).
  • assign and unassign call repo.issues.update(n, assignees: [name]) and assignees: [].
  • edit(slug, n, title, body) calls repo.issues.update(n, body:) and adds title: only when a title is given.
  • Label operations take numeric IDs. The backend resolves names through repo.labels.list. An unknown name raises Issues::CommandFailed that names the label.
  • New method open_issues(slug) returns { number:, title:, labels: [names], body:, url: } rows for open issues. InspectCommand and bin/wakeup-poll use it.
  • The backend rescues ::Forgejo::Error and raises Issues::CommandFailed with the error message. ::Forgejo::ApiError#status and the message carry the HTTP status. Callers already rescue CommandFailed.

Boot (ADR-0166), modeled on plugins/os-sdlc: os-backlog gets its own Gemfile and a committed Gemfile.lock.
The single boot file is plugins/os-backlog/lib/backlog.rb.
It sets ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) and requires bundler/setup before require 'zeitwerk'.
plugins/os-backlog/bin/os-backlog, bin/wakeup-poll, bin/decision-sweep, and tests/test_helper.rb already load that file, so they need no other boot line.
The Gemfile is modeled on plugins/os-sdlc/Gemfile: ruby "4.0.2", thor, zeitwerk, the forgejo-api line from ADR-0257, and minitest in the :development, :test group.

Test seam (same lesson as #858, commit 67d32857): tests inject a fake client and start no process and no network.
Model the fakes on plugins/os-sdlc/tests/support/fake_forgejo_client.rb and plugins/os-sdlc/tests/support/fake_forgejo_repo.rb.
Create os-backlog copies under plugins/os-backlog/tests/support/, with the extra repo methods this backend calls (issue create/list/update, comment list, label list/remove/replace).
Backlog::CLI.start takes an optional client: in its config hash, next to runner:, and passes it to Issues, IssueShow, and InspectCommand.
bin/os-backlog does not pass it.
os-backlog tests use parallelize_me! (threads), not minitest-parallel_fork, so no Marshal issue applies.
tests/test_helper.rb still requires forgejo so that tests can name ::Forgejo::NotFoundError without an autoload race.

Required work:

  1. Add plugins/os-backlog/Gemfile and a committed plugins/os-backlog/Gemfile.lock with the forgejo-api line from ADR-0257, and load them in plugins/os-backlog/lib/backlog.rb as described in the Boot paragraph. check: present bundler/setup in plugins/os-backlog/lib/backlog.rb
  2. Add plugins/os-backlog/lib/backlog/forgejo_backend.rb with the method mapping above. check: present ::Forgejo::Client in plugins/os-backlog/lib/backlog/forgejo_backend.rb
  3. Route forgejo: trackers to ForgejoBackend in plugins/os-backlog/lib/backlog/issues.rb (rename the @tea_backend ivar and the backends[:tea] key) and add the client: keyword through Issues, Backlog::CLI.start, and cli_command_helpers.rb. check: absent TeaBackend in plugins/os-backlog/lib/backlog/issues.rb
  4. Delete plugins/os-backlog/lib/backlog/tea_backend.rb. If CommandRunner has no caller left except GhBackend, keep it for GhBackend; drop only the tea-specific parts. check: absent class TeaBackend in plugins/os-backlog/lib/backlog/*.rb
  5. Read the Forgejo path of plugins/os-backlog/lib/backlog/issue_show.rb through the backend or the injected client and remove its tea argv and the tea comment. check: absent "tea" in plugins/os-backlog/lib/backlog/issue_show.rb
  6. In plugins/os-backlog/lib/backlog/inspect_command.rb, replace the tea methods with a call to open_issues. Remove tea_unavailable_result and the line-count fallback. Report "tool" => "forgejo". A ::Forgejo::Error gives "open_count" => nil and a "reason". check: absent "tea" in plugins/os-backlog/lib/backlog/inspect_command.rb
  7. In plugins/os-backlog/bin/wakeup-poll, list Forgejo issues through open_issues and remove the tea argv and the "is tea logged in" text. check: absent tea in plugins/os-backlog/bin/wakeup-poll
  8. Migrate plugins/os-backlog/tests/issue_show_forgejo_test.rb to the injected fake client (same three behaviors: normalized issue, error, no non-JSON case).
  9. Migrate the Forgejo tests in plugins/os-backlog/tests/issues_create_test.rb, plugins/os-backlog/tests/issues_create_with_labels_test.rb, plugins/os-backlog/tests/issues_list_test.rb, plugins/os-backlog/tests/issues_scope_labels_test.rb, and plugins/os-backlog/tests/issues_close_protocol_test.rb to the injected fake client. Keep their github: and repo: tests unchanged.
  10. Migrate plugins/os-backlog/tests/inspect_command_tea_json_test.rb and plugins/os-backlog/tests/inspect_command_executable_path_test.rb to the injected fake client. The ExecutablePath tests keep only what gh still needs: no tea CLI not found case, and the no-which test stays.
  11. Migrate the Forgejo cases in the subprocess CLI tests plugins/os-backlog/tests/issue_edit_cli_test.rb, plugins/os-backlog/tests/labels_cli_contract_test.rb, plugins/os-backlog/tests/issue_create_body_file_cli_test.rb, plugins/os-backlog/tests/issue_create_scope_gate_cli_test.rb, plugins/os-backlog/tests/issue_create_origin_gate_cli_test.rb, and plugins/os-backlog/tests/issue_create_template_gate_cli_test.rb from a fake tea on PATH to in-process Backlog::CLI.start(..., client: fake) (the pattern of plugins/os-backlog/tests/cli_issue_create_scope_gate_test.rb). Assert the body, title, and label IDs that reach the fake repo. Keep the github: and repo: cases unchanged.
  12. Remove the fake-tea support code: FAKE_TEA_* and install_fake_tea* and with_forgejo_tracker tea wiring in plugins/os-backlog/tests/support/labels_cli_helpers.rb, plugins/os-backlog/tests/support/inspect_fake_tea_json_runner.rb, plugins/os-backlog/tests/support/inspect_fake_tea_fallback_runner.rb, and plugins/os-backlog/tests/support/inspect_recording_tea_runner.rb. Keep plugins/os-backlog/tests/support/no_real_tracker_commands.rb and plugins/os-backlog/tests/shell_runner_refuses_tracker_commands_test.rb unchanged: gh still uses the guard. check: absent install_fake_tea in plugins/os-backlog/tests/support/labels_cli_helpers.rb
  13. Change the capture skill line that says issue-create creates the issue via tea so that it says the Forgejo API client. check: present Forgejo API client in plugins/os-backlog/skills/capture/SKILL.md

Rejected: keep tea and add --output json where it exists. tea issues create has none (ADR-0256).

Rejected: a second Forgejo client such as forgejo-client. ADR-0257 makes forgejo-api the only one.

Acceptance criteria

  1. Given a forgejo: tracker and a fake client, when issue-create runs with a body that starts with --label, then the fake repo receives that body unchanged and the command prints the new number and URL.
  2. Given a forgejo: tracker, when issue-show, comment, close, issue-edit, and label add/remove run, then each one calls the matching repo.issues or repo.comments method on the client and no tea process starts.
  3. Given FORGEJO_TOKEN is not set and no client is injected, when any Forgejo command runs, then it exits non-zero with a message that names FORGEJO_TOKEN.
  4. Given the client raises ::Forgejo::ApiError, when a command runs, then the command prints the error message (it starts with the HTTP status) and exits non-zero.
  5. Given wakeup-poll runs for a forgejo: project, when open issues exist, then it lists them with number, title, labels, body, and URL, as it does today.
  6. Given inspect runs on a Forgejo repository, when open issues exist, then it reports their count with "tool" => "forgejo", and no line-count fallback exists.
  7. Given a github: or repo: tracker, when any command runs without FORGEJO_TOKEN, then it behaves as before.
  8. ruby plugins/os-backlog/tests/all.rb (the os-backlog target in .sdlc/project.yaml) passes.

Owner sketch

class Backlog::ForgejoBackend
  def initialize(client: nil)   # nil: built lazily from ENV.fetch("FORGEJO_TOKEN")
  def create(slug, title:, body:, labels: [])   # => { number:, url: }
  def fetch(slug, number)                       # => { number:, title:, labels:, body: }
  def open_issues(slug)                         # => [{ number:, title:, labels:, body:, url: }]
  # ... the rest of TeaBackend's public methods, same shapes
end

Non-goals

  • GitHub (gh). Its move to octokit is #857.
  • os-sdlc's tea calls. They have their own ticket.
  • The tea labels create commands in plugins/os-backlog/skills/route/SKILL.md. ForgejoBackend has no label-create method in this ticket.
  • The root CLAUDE.md "Use the tea CLI" line is outside the os-backlog target (ADR-0255); the orchestrator updates it by hand when this ticket closes.

Blocked by

  • None. #855 (the forgejo-api gem, ADR-0257) is closed. The git-gem dependency work is not a blocker.

Retired tests

plugins/os-backlog/tests/inspect_command_tea_fallback_test.rb

Origin

  • Trigger: #845 audit; ADR-0256 decided 2026-09-30.
  • Improvised this session: none
  • Chain: ticket text in argv and a regex on create output ← TeaBackend shells out to tea ← DESIGN: ADR-0123, amended by ADR-0256 and ADR-0257.
  • Root candidate: this ticket
  • Where: plugins/os-backlog/lib/backlog/tea_backend.rb, issue_show.rb, inspect_command.rb, bin/wakeup-poll
  • Session: f090dd39-8ad6-4f4e-8743-7bbedb8f08d4
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/f090dd39-8ad6-4f4e-8743-7bbedb8f08d4.jsonl
## Problem os-backlog runs the `tea` CLI for every Forgejo operation. Ticket titles, bodies, and comments go through argv. `tea issue create` prints text, so `CommandRunner` extracts the number and URL with a regex. ADR-0256 replaces tea with an in-process Forgejo REST client. ADR-0257 names that client: the `forgejo-api` gem is the only Forgejo client. ``` plugins/os-backlog/lib/backlog/tea_backend.rb:7-9 tea issue create --repo S --title T --description B [--labels a,b] (regex on stdout) plugins/os-backlog/lib/backlog/tea_backend.rb:38 tea comment --repo S N BODY plugins/os-backlog/lib/backlog/issue_show.rb:27 tea issues N --repo S -o json plugins/os-backlog/lib/backlog/inspect_command.rb:73,80 tea issues list ... (JSON, then a line-count fallback) plugins/os-backlog/bin/wakeup-poll:15,40 tea issues list --repo S --state open --output json ``` `tea_backend.rb` has 14 tea command shapes in total: create, labels list, issues list, issue view, comment, close, comment ls, label add, label remove, assign, unassign, and edit. ## Reproduce n/a: design change from ADR-0256 and ADR-0257. ## Approach A new `Backlog::ForgejoBackend` keeps the public contract of `TeaBackend` (same methods, same argument order with `repo_slug` first, same return shapes) and delegates each call to `::Forgejo::Client`. Callers and `GhBackend` do not change. `Issues#route` builds it for `forgejo:` trackers. The client is an injected `client:` keyword. The default is built lazily on first Forgejo call: `::Forgejo::Client.new(url: "https://forgejo.swansoncloud.com", api_key: ENV.fetch("FORGEJO_TOKEN"))`. A `github:` or `repo:` tracker never needs `FORGEJO_TOKEN`. `IssueShow`, `InspectCommand`, and `bin/wakeup-poll` read through the same backend. `TeaBackend` and its tea argv builders are deleted. Method mapping (`repo = client.repo(owner, name)`, from the `owner/name` slug): - `create(slug, title:, body:, labels:)` calls `repo.issues.create(title:, body:, labels: [ids])` and returns `{ number: issue.number, url: issue.html_url }`. - `labels_list(slug)` reads `repo.labels.list` and returns `[{ name:, description: }]`. - `list(slug)` reads `repo.issues.list(state: "open", type: "issues")` (drain the lazy page with `to_a`) and returns `{ number:, title:, labels: [names], assignee: }` rows. - `labels(slug, n)` and `fetch(slug, n)` read `repo.issues.get(n)`. - `comment` calls `repo.comments.create(n, body:)`. `comments` maps `repo.comments.list_for_issue(n)` to bodies. - `close` calls `repo.issues.update(n, state: "closed")`. `closed?` reads `repo.issues.get(n)`. - `add_label` and `remove_label` call `repo.issues.labels(n).add(labels: [id])` and `.remove(id)`. - `assign` and `unassign` call `repo.issues.update(n, assignees: [name])` and `assignees: []`. - `edit(slug, n, title, body)` calls `repo.issues.update(n, body:)` and adds `title:` only when a title is given. - Label operations take numeric IDs. The backend resolves names through `repo.labels.list`. An unknown name raises `Issues::CommandFailed` that names the label. - New method `open_issues(slug)` returns `{ number:, title:, labels: [names], body:, url: }` rows for open issues. `InspectCommand` and `bin/wakeup-poll` use it. - The backend rescues `::Forgejo::Error` and raises `Issues::CommandFailed` with the error message. `::Forgejo::ApiError#status` and the message carry the HTTP status. Callers already rescue `CommandFailed`. Boot (ADR-0166), modeled on `plugins/os-sdlc`: os-backlog gets its own Gemfile and a committed Gemfile.lock. The single boot file is `plugins/os-backlog/lib/backlog.rb`. It sets `ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)` and requires `bundler/setup` before `require 'zeitwerk'`. `plugins/os-backlog/bin/os-backlog`, `bin/wakeup-poll`, `bin/decision-sweep`, and `tests/test_helper.rb` already load that file, so they need no other boot line. The Gemfile is modeled on `plugins/os-sdlc/Gemfile`: `ruby "4.0.2"`, `thor`, `zeitwerk`, the `forgejo-api` line from ADR-0257, and `minitest` in the `:development, :test` group. Test seam (same lesson as #858, commit 67d32857): tests inject a fake client and start no process and no network. Model the fakes on `plugins/os-sdlc/tests/support/fake_forgejo_client.rb` and `plugins/os-sdlc/tests/support/fake_forgejo_repo.rb`. Create os-backlog copies under `plugins/os-backlog/tests/support/`, with the extra repo methods this backend calls (issue create/list/update, comment list, label list/remove/replace). `Backlog::CLI.start` takes an optional `client:` in its config hash, next to `runner:`, and passes it to `Issues`, `IssueShow`, and `InspectCommand`. `bin/os-backlog` does not pass it. os-backlog tests use `parallelize_me!` (threads), not `minitest-parallel_fork`, so no Marshal issue applies. `tests/test_helper.rb` still requires `forgejo` so that tests can name `::Forgejo::NotFoundError` without an autoload race. Required work: 1. Add `plugins/os-backlog/Gemfile` and a committed `plugins/os-backlog/Gemfile.lock` with the `forgejo-api` line from ADR-0257, and load them in `plugins/os-backlog/lib/backlog.rb` as described in the Boot paragraph. check: present `bundler/setup` in `plugins/os-backlog/lib/backlog.rb` 2. Add `plugins/os-backlog/lib/backlog/forgejo_backend.rb` with the method mapping above. check: present `::Forgejo::Client` in `plugins/os-backlog/lib/backlog/forgejo_backend.rb` 3. Route `forgejo:` trackers to `ForgejoBackend` in `plugins/os-backlog/lib/backlog/issues.rb` (rename the `@tea_backend` ivar and the `backends[:tea]` key) and add the `client:` keyword through `Issues`, `Backlog::CLI.start`, and `cli_command_helpers.rb`. check: absent `TeaBackend` in `plugins/os-backlog/lib/backlog/issues.rb` 4. Delete `plugins/os-backlog/lib/backlog/tea_backend.rb`. If `CommandRunner` has no caller left except `GhBackend`, keep it for `GhBackend`; drop only the tea-specific parts. check: absent `class TeaBackend` in `plugins/os-backlog/lib/backlog/*.rb` 5. Read the Forgejo path of `plugins/os-backlog/lib/backlog/issue_show.rb` through the backend or the injected client and remove its `tea` argv and the tea comment. check: absent `"tea"` in `plugins/os-backlog/lib/backlog/issue_show.rb` 6. In `plugins/os-backlog/lib/backlog/inspect_command.rb`, replace the tea methods with a call to `open_issues`. Remove `tea_unavailable_result` and the line-count fallback. Report `"tool" => "forgejo"`. A `::Forgejo::Error` gives `"open_count" => nil` and a `"reason"`. check: absent `"tea"` in `plugins/os-backlog/lib/backlog/inspect_command.rb` 7. In `plugins/os-backlog/bin/wakeup-poll`, list Forgejo issues through `open_issues` and remove the `tea` argv and the "is tea logged in" text. check: absent `tea` in `plugins/os-backlog/bin/wakeup-poll` 8. Migrate `plugins/os-backlog/tests/issue_show_forgejo_test.rb` to the injected fake client (same three behaviors: normalized issue, error, no non-JSON case). 9. Migrate the Forgejo tests in `plugins/os-backlog/tests/issues_create_test.rb`, `plugins/os-backlog/tests/issues_create_with_labels_test.rb`, `plugins/os-backlog/tests/issues_list_test.rb`, `plugins/os-backlog/tests/issues_scope_labels_test.rb`, and `plugins/os-backlog/tests/issues_close_protocol_test.rb` to the injected fake client. Keep their `github:` and `repo:` tests unchanged. 10. Migrate `plugins/os-backlog/tests/inspect_command_tea_json_test.rb` and `plugins/os-backlog/tests/inspect_command_executable_path_test.rb` to the injected fake client. The `ExecutablePath` tests keep only what `gh` still needs: no `tea` CLI not found case, and the no-`which` test stays. 11. Migrate the Forgejo cases in the subprocess CLI tests `plugins/os-backlog/tests/issue_edit_cli_test.rb`, `plugins/os-backlog/tests/labels_cli_contract_test.rb`, `plugins/os-backlog/tests/issue_create_body_file_cli_test.rb`, `plugins/os-backlog/tests/issue_create_scope_gate_cli_test.rb`, `plugins/os-backlog/tests/issue_create_origin_gate_cli_test.rb`, and `plugins/os-backlog/tests/issue_create_template_gate_cli_test.rb` from a fake `tea` on PATH to in-process `Backlog::CLI.start(..., client: fake)` (the pattern of `plugins/os-backlog/tests/cli_issue_create_scope_gate_test.rb`). Assert the body, title, and label IDs that reach the fake repo. Keep the `github:` and `repo:` cases unchanged. 12. Remove the fake-tea support code: `FAKE_TEA_*` and `install_fake_tea*` and `with_forgejo_tracker` tea wiring in `plugins/os-backlog/tests/support/labels_cli_helpers.rb`, `plugins/os-backlog/tests/support/inspect_fake_tea_json_runner.rb`, `plugins/os-backlog/tests/support/inspect_fake_tea_fallback_runner.rb`, and `plugins/os-backlog/tests/support/inspect_recording_tea_runner.rb`. Keep `plugins/os-backlog/tests/support/no_real_tracker_commands.rb` and `plugins/os-backlog/tests/shell_runner_refuses_tracker_commands_test.rb` unchanged: `gh` still uses the guard. check: absent `install_fake_tea` in `plugins/os-backlog/tests/support/labels_cli_helpers.rb` 13. Change the capture skill line that says `issue-create` creates the issue via `tea` so that it says the Forgejo API client. check: present `Forgejo API client` in `plugins/os-backlog/skills/capture/SKILL.md` Rejected: keep tea and add `--output json` where it exists. `tea issues create` has none (ADR-0256). Rejected: a second Forgejo client such as `forgejo-client`. ADR-0257 makes `forgejo-api` the only one. ## Acceptance criteria 1. Given a `forgejo:` tracker and a fake client, when `issue-create` runs with a body that starts with `--label`, then the fake repo receives that body unchanged and the command prints the new number and URL. 2. Given a `forgejo:` tracker, when `issue-show`, `comment`, `close`, `issue-edit`, and label add/remove run, then each one calls the matching `repo.issues` or `repo.comments` method on the client and no `tea` process starts. 3. Given `FORGEJO_TOKEN` is not set and no client is injected, when any Forgejo command runs, then it exits non-zero with a message that names `FORGEJO_TOKEN`. 4. Given the client raises `::Forgejo::ApiError`, when a command runs, then the command prints the error message (it starts with the HTTP status) and exits non-zero. 5. Given `wakeup-poll` runs for a `forgejo:` project, when open issues exist, then it lists them with number, title, labels, body, and URL, as it does today. 6. Given `inspect` runs on a Forgejo repository, when open issues exist, then it reports their count with `"tool" => "forgejo"`, and no line-count fallback exists. 7. Given a `github:` or `repo:` tracker, when any command runs without `FORGEJO_TOKEN`, then it behaves as before. 8. `ruby plugins/os-backlog/tests/all.rb` (the `os-backlog` target in `.sdlc/project.yaml`) passes. ## Owner sketch ```ruby class Backlog::ForgejoBackend def initialize(client: nil) # nil: built lazily from ENV.fetch("FORGEJO_TOKEN") def create(slug, title:, body:, labels: []) # => { number:, url: } def fetch(slug, number) # => { number:, title:, labels:, body: } def open_issues(slug) # => [{ number:, title:, labels:, body:, url: }] # ... the rest of TeaBackend's public methods, same shapes end ``` ## Non-goals - GitHub (`gh`). Its move to octokit is #857. - os-sdlc's tea calls. They have their own ticket. - The `tea labels create` commands in `plugins/os-backlog/skills/route/SKILL.md`. `ForgejoBackend` has no label-create method in this ticket. - The root CLAUDE.md "Use the `tea` CLI" line is outside the os-backlog target (ADR-0255); the orchestrator updates it by hand when this ticket closes. ## Blocked by - None. #855 (the `forgejo-api` gem, ADR-0257) is closed. The git-gem dependency work is not a blocker. ## Retired tests plugins/os-backlog/tests/inspect_command_tea_fallback_test.rb ## Origin - Trigger: #845 audit; ADR-0256 decided 2026-09-30. - Improvised this session: none - Chain: ticket text in argv and a regex on create output ← TeaBackend shells out to tea ← DESIGN: ADR-0123, amended by ADR-0256 and ADR-0257. - Root candidate: this ticket - Where: plugins/os-backlog/lib/backlog/tea_backend.rb, issue_show.rb, inspect_command.rb, bin/wakeup-poll - Session: f090dd39-8ad6-4f4e-8743-7bbedb8f08d4 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/f090dd39-8ad6-4f4e-8743-7bbedb8f08d4.jsonl
Author
Owner

Live evidence, 2026-09-30: os-backlog issue-create for #855 printed "url": "https://<host>/api/v1/....". The regex that reads tea issue create output matched a URL inside the ticket body, not the created issue URL. Acceptance criterion 1 covers this.

Live evidence, 2026-09-30: `os-backlog issue-create` for #855 printed `"url": "https://<host>/api/v1/...."`. The regex that reads `tea issue create` output matched a URL inside the ticket body, not the created issue URL. Acceptance criterion 1 covers this.
Author
Owner

Update 2026-09-30: the Forgejo client is now the generated forgejo-api gem (ADR-0257, commit b68750db), not forgejo-client. Source: gem "forgejo-api", git: "ssh://git@forgejo.swansoncloud.com:2222/jared/forgejo-api.git", require: "forgejo". Client: Forgejo::Client.new(url: "https://forgejo.swansoncloud.com", api_key: ENV.fetch("FORGEJO_TOKEN")); repo = client.repo(owner, name); issues repo.issues.create/get/list/update, comments repo.comments.create(n, body:), issue labels repo.issues.labels(n).add(labels: [ids])/.remove(id)/.replace, repo labels repo.labels.list. Label operations take IDs, so the backend resolves names through repo.labels.list. os-backlog has no Gemfile yet; ADR-0166 already decides the loading method (per-plugin Gemfile + lockfile + one boot file), so this ticket's first step adds it. The git-gem work is not a blocker for this ticket.

Update 2026-09-30: the Forgejo client is now the generated `forgejo-api` gem (ADR-0257, commit b68750db), not `forgejo-client`. Source: `gem "forgejo-api", git: "ssh://git@forgejo.swansoncloud.com:2222/jared/forgejo-api.git", require: "forgejo"`. Client: `Forgejo::Client.new(url: "https://forgejo.swansoncloud.com", api_key: ENV.fetch("FORGEJO_TOKEN"))`; `repo = client.repo(owner, name)`; issues `repo.issues.create/get/list/update`, comments `repo.comments.create(n, body:)`, issue labels `repo.issues.labels(n).add(labels: [ids])/.remove(id)/.replace`, repo labels `repo.labels.list`. Label operations take IDs, so the backend resolves names through `repo.labels.list`. os-backlog has no Gemfile yet; ADR-0166 already decides the loading method (per-plugin Gemfile + lockfile + one boot file), so this ticket's first step adds it. The git-gem work is not a blocker for this ticket.
jared changed title from Replace tea with forgejo-client in os-backlog to Replace tea with forgejo-api in os-backlog 2026-09-30 21:06:16 +00:00
Author
Owner

Work started 2026-09-30 on branch ticket-856 (worktree .claude/worktrees/ticket-856), os-sdlc pipeline, target os-backlog.

Work started 2026-09-30 on branch ticket-856 (worktree .claude/worktrees/ticket-856), os-sdlc pipeline, target os-backlog.
Author
Owner

--label check 2026-09-30: this comment was posted through the new ForgejoBackend (forgejo-api, worktree ticket-856); a body that starts with --label is stored unchanged.

--label check 2026-09-30: this comment was posted through the new ForgejoBackend (forgejo-api, worktree ticket-856); a body that starts with --label is stored unchanged.
Author
Owner

Resolution

Done: os-backlog's Forgejo backend is ForgejoBackend over forgejo-api in process (ADR-0257); TeaBackend deleted; issue-show, inspect and wakeup-poll read through it; os-backlog has a Gemfile/lockfile and fails loudly with a bundle-install hint when gems are missing; CLAUDE.md now says to use the os-backlog CLI.

Evidence: main fc9ba80e. os-backlog suite 286 runs, 0 failures, 0 errors. Live on forgejo.swansoncloud.com through the worktree CLI: issue-show 856 (labels as names), labels (10), comment with a body starting with --label posted, issue-show 999999 -> HTTP 404, missing FORGEJO_TOKEN named. Installed plugin after merge: bundle check OK, issue-show 856 OK, and this close ran through it. Implementation 18 escalated at programmer-repair (stale tea tests, #863); hand-finished, including a nested-Hash label bug the fakes had hidden.

Follow-ups: #863 no pipeline role migrates existing tests (skeptic CREATE). #857 gh to octokit (existing). os-backlog has no rubocop in its bundle and no lint_config in .sdlc/project.yaml; 23 Metrics offenses exist under the system rubocop: dropped, lint is not gated for this target. Each machine needs bundle install in plugins/os-backlog. The route skill still documents tea labels create (Non-goal of this ticket): dropped here, belongs with a label-create backend method when needed.

## Resolution **Done:** os-backlog's Forgejo backend is ForgejoBackend over forgejo-api in process (ADR-0257); TeaBackend deleted; issue-show, inspect and wakeup-poll read through it; os-backlog has a Gemfile/lockfile and fails loudly with a bundle-install hint when gems are missing; CLAUDE.md now says to use the os-backlog CLI. **Evidence:** main fc9ba80e. os-backlog suite 286 runs, 0 failures, 0 errors. Live on forgejo.swansoncloud.com through the worktree CLI: issue-show 856 (labels as names), labels (10), comment with a body starting with --label posted, issue-show 999999 -> HTTP 404, missing FORGEJO_TOKEN named. Installed plugin after merge: bundle check OK, issue-show 856 OK, and this close ran through it. Implementation 18 escalated at programmer-repair (stale tea tests, #863); hand-finished, including a nested-Hash label bug the fakes had hidden. **Follow-ups:** #863 no pipeline role migrates existing tests (skeptic CREATE). #857 gh to octokit (existing). os-backlog has no rubocop in its bundle and no lint_config in .sdlc/project.yaml; 23 Metrics offenses exist under the system rubocop: dropped, lint is not gated for this target. Each machine needs bundle install in plugins/os-backlog. The route skill still documents tea labels create (Non-goal of this ticket): dropped here, belongs with a label-create backend method when needed.
jared closed this issue 2026-09-30 21:31:20 +00:00
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
jared/cc-os#856
No description provided.