Skip to content

refactor(#5988): introduce tracker.Client interface with forge adapter - #5993

Merged
ralphbean merged 11 commits into
mainfrom
agent/5988-tracker-client
Aug 10, 2026
Merged

refactor(#5988): introduce tracker.Client interface with forge adapter#5993
ralphbean merged 11 commits into
mainfrom
agent/5988-tracker-client

Conversation

@ralphbean

@ralphbean ralphbean commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds internal/tracker, a narrower interface for issue-content read/write (title, body, comments) keyed by (project string, number int) instead of (owner, repo string, number int). This lets a future Jira implementation use its natural PROJECT-123 key instead of a forced owner/repo split.
  • ForgeClient adapts any forge.Client (GitHub or GitLab) to tracker.Client by splitting project back into owner/repo — since forge.Client already abstracts GitHub vs GitLab, one adapter covers both.
  • Pure interface + adapter, reviewable in isolation. Nothing calls tracker.Client yet.
  • UpdateComment takes an explicit number int parameter in addition to commentID, which isn't in issue Introduce tracker.Client: a forge-agnostic issue-content interface for GitHub/GitLab #5988's original spec. This is a deliberate deviation: Jira's REST API needs the issue key (project+number) to update a comment, not just a comment ID, so the interface has to carry number here even though today's GitHub/GitLab adapter ignores it.
  • FakeClient.UpdateIssueComment is changed from always returning nil to returning fmt.Errorf("%w: comment %d", ErrNotFound, commentID) when the comment ID isn't found.

Closes #5988. Step toward #5989 (Jira tracker.Client implementation) and #5991 (fullsend issues get/post-comment CLI).

Test plan

  • go test ./internal/tracker/... (new tests, TDD: written first and confirmed failing before implementation)
  • go build ./...
  • go vet ./internal/tracker/...
  • make lint
  • go test ./... (pre-existing unrelated failure in internal/scaffold TestFileModeMatchesFilesystem, confirmed present on main before this change)

🤖 Generated with Claude Code

Adds internal/tracker, a narrower interface for issue-content read/write
(title, body, comments) keyed by (project string, number int) instead of
(owner, repo string, number int). This lets Jira, whose issues are keyed
as PROJECT-123, implement the same interface later without a forced
owner/repo split. forge.Client stays scoped to git-hosting operations.

ForgeClient adapts any forge.Client (GitHub or GitLab) to tracker.Client
by splitting the project string back into owner/repo. No behavior change:
nothing calls tracker.Client yet.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@ralphbean
ralphbean requested a review from a team as a code owner August 6, 2026 18:26
@ralphbean ralphbean added the fullsend-fix Enables automatic bot-triggered fix runs on human-authored PRs label Aug 6, 2026
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add tracker.Client interface and ForgeClient adapter

✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Introduce tracker.Client for issue content read/write keyed by (project, number).
• Add ForgeClient adapter to bridge tracker.Client to existing forge.Client APIs.
• Add unit tests covering project parsing and comment/issue operations via fake forge.
Diagram

graph TD
A["Future callers"] --> B["tracker.Client"] --> C["ForgeClient"] --> D["forge.Client"] --> E["GitHub/GitLab"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Extend forge.Client to use (project, number) everywhere
  • ➕ Avoids introducing a new interface/package
  • ➕ Removes need for project splitting adapter
  • ➖ Expands forge.Client beyond git-hosting concerns (hurts Jira fit)
  • ➖ Higher churn/risk across existing forge implementations and call sites
2. Use a structured ProjectKey type (e.g., {Owner, Repo, Raw})
  • ➕ Avoids lossy string parsing and clarifies semantics per backend
  • ➕ Allows validation/normalization per provider
  • ➖ More types and plumbing for callers and adapters
  • ➖ Still requires decisions about Jira key vs owner/repo representation

Recommendation: Current approach (new tracker.Client plus a thin ForgeClient adapter) is the best incremental step: it isolates the new abstraction, keeps forge.Client scoped to git-hosting, and enables a future Jira implementation without forcing owner/repo semantics. Consider introducing a structured ProjectKey later only if multiple backends require richer parsing/validation than a single project string.

Files changed (3) +296 / -0

Enhancement (2) +148 / -0
forge_client.goAdd ForgeClient adapter over forge.Client +99/-0

Add ForgeClient adapter over forge.Client

• Implements tracker.Client by delegating to forge.Client after splitting project into owner/repo (supports GitHub and nested GitLab namespaces). Converts forge.IssueComment numeric IDs to string IDs and validates numeric IDs on update.

internal/tracker/forge_client.go

tracker.goDefine tracker.Client interface and shared Issue/Comment types +49/-0

Define tracker.Client interface and shared Issue/Comment types

• Introduces a narrow, forge-agnostic interface for reading/writing issue content and comments keyed by (project, number). Uses string comment IDs to support non-numeric trackers (e.g., Jira).

internal/tracker/tracker.go

Tests (1) +148 / -0
tracker_test.goAdd unit tests for ForgeClient adapter and project parsing +148/-0

Add unit tests for ForgeClient adapter and project parsing

• Tests splitProject behavior for GitHub and nested GitLab-style namespaces. Verifies ForgeClient issue retrieval, comment creation/listing/update flows, and invalid comment ID handling using forge.NewFakeClient.

internal/tracker/tracker_test.go

@qodo-code-review

qodo-code-review Bot commented Aug 6, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Invalid project accepted ✓ Resolved 🐞 Bug ≡ Correctness
Description
splitProject can return an empty owner or repo (or no owner at all) for inputs like "project",
"/repo", or "owner/", and ForgeClient forwards those values into forge.Client calls that
require both owner and repo, leading to invalid requests or misleading NotFound errors.
Code

internal/tracker/forge_client.go[R95-98]

+	if idx < 0 {
+		return "", project
+	}
+	return project[:idx], project[idx+1:]
Relevance

●●● Strong

Team previously accepted strict owner/repo validation to avoid malformed requests; same pattern
applies to splitProject inputs.

PR-#2197
PR-#2370

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The adapter can produce empty owner/repo via splitProject, but downstream forge methods require
both owner and repo and build API paths from them, so malformed project values will translate into
invalid requests/paths.

internal/tracker/forge_client.go[88-99]
internal/forge/forge.go[514-523]
internal/forge/github/github.go[2482-2490]
internal/forge/gitlab/gitlab.go[144-148]
internal/config/config.go[748-764]
PR-#2197

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ForgeClient` adapts `tracker`'s single `project` string into the `(owner, repo)` pair required by `forge.Client`, but `splitProject()` currently accepts malformed strings and can produce empty `owner` and/or `repo`. This can cause invalid API calls (e.g. `/repos//repo/...` on GitHub) and makes failures harder to diagnose.

### Issue Context
- `forge.Client`’s issue/comment methods require `owner` and `repo` parameters.
- The repo already enforces strict non-empty `owner/name` validation elsewhere; this adapter should enforce similar constraints at the boundary.
- Must still support GitLab nested namespaces like `group/subgroup/project` by splitting on the last `/`.

### Fix Focus Areas
- internal/tracker/forge_client.go[28-76]
- internal/tracker/forge_client.go[88-99]
- internal/tracker/tracker_test.go[10-27]

### Suggested change
1. Replace `splitProject(project string) (owner, repo string)` with a parsing helper that can fail, e.g.:
  - `parseForgeProject(project string) (owner, repo string, err error)`
  - Split on last `/`.
  - Require both `owner` and `repo` to be non-empty.
  - (Optional) Reject empty path segments (e.g. `group//project`) if desired.
2. In each `ForgeClient` method, call the parser and return a clear error when invalid (include the original `project` string).
3. Update tests:
  - Remove/adjust the `"project"` case.
  - Add cases for `"/repo"`, `"owner/"`, and `""` asserting an error.

### Notes
This aligns with existing strict owner/name validation patterns in config.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Jira key docs inconsistent ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
Package docs say Jira can use the full issue key PROJECT-123 as the project string, but the
Client interface also requires a separate number int and later describes project as only the
Jira project key, creating an ambiguous contract for future callers/implementations.
Code

internal/tracker/tracker.go[R8-11]

+// (it has no branches, pull requests, or CI). Keying by a single project
+// string lets a future Jira implementation use its natural issue key
+// (PROJECT-123) instead of forcing an owner/repo split that Jira doesn't
+// have.
Relevance

●●● Strong

They often accept fixing doc/comment contract mismatches to reduce ambiguity for future callers.

PR-#2630
PR-#5615
PR-#5555

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The same file’s comments describe project as both a full Jira issue key and as a Jira project key,
while the interface includes a separate numeric number, making the intended Jira mapping unclear.

internal/tracker/tracker.go[6-11]
internal/tracker/tracker.go[41-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`internal/tracker` package documentation describes two different Jira identifier models:
- `project` as the full issue key (e.g. `PROJECT-123`)
- `project` as the Jira project key (e.g. `PROJECT`) with `number` as the issue number

This inconsistency can mislead future Jira implementations and callers.

### Issue Context
The `Client` interface is `GetIssue(ctx, project string, number int)` etc., so docs should unambiguously define what values are expected for Jira.

### Fix Focus Areas
- internal/tracker/tracker.go[1-15]
- internal/tracker/tracker.go[41-49]

### Suggested change
Choose one contract and update the comments accordingly. For example:
- If Jira is meant to use `(projectKey, issueNumber)`, update the top-level comment to remove `PROJECT-123` and describe `PROJECT` + `123`.
- If Jira is meant to use the full issue key, consider redesigning the interface (e.g., accept `issueKey string` and/or make `number` optional), then update docs and tests consistently.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 54 rules

Grey Divider

Tip of the day
💡 Did you know, you can reply 'qodo' on any finding to push back, ask questions, or dig deeper

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread internal/tracker/forge_client.go Outdated
Comment thread internal/tracker/tracker.go Outdated

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review squad pass (Claude + Grok, cross-verified against issue #5989 and the existing internal/forge/gitlab/internal/forge/jira implementations). Posting the Medium+ findings that aren't already covered by the existing qodo-code-review comments on this PR (the "invalid project accepted" and "Jira key docs inconsistent" threads already capture two other issues we found independently and are not re-posted here).

One more Medium finding with no single line to anchor to: per this repo's COMMITS.md, adding an internal interface with no user-visible behavior change (the PR body says "nothing calls tracker.Client yet") should be refactor(#5988): ..., not feat(#5988): ...feat titles surface directly in release notes under Features.

Comment thread internal/tracker/tracker.go Outdated
Comment thread internal/tracker/tracker.go
Comment thread internal/tracker/tracker.go Outdated
Jira's comment update endpoint (PUT /issue/{issueIdOrKey}/comment/{commentId})
needs the issue key, not just a comment ID, unlike GitHub/GitLab where a
comment ID alone is enough. Add number to UpdateComment so a future Jira
tracker.Client can reconstruct PROJECT-123; ForgeClient ignores it since
forge.Client.UpdateIssueComment doesn't need it.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
splitProject now returns an error for malformed project strings
("project", "/repo", "owner/") instead of silently forwarding an
empty owner or repo into forge.Client calls. Also fixes two doc
comments in tracker.go: the package doc described a full Jira issue
key ("PROJECT-123") as the project string, contradicting the
interface's separate number param; and the Comment.ID doc claimed
UpdateComment could delete a comment (there's no DeleteComment) and
overstated that comment IDs are non-numeric across trackers.

Addresses review feedback from qodo-code-review and waynesun09 on
PR #5993.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@ralphbean

Copy link
Copy Markdown
Member Author

Same two findings as the inline review comments — fixed both in 70a15d6. Referencing this comment: #5993 (comment)

Comment thread internal/tracker/tracker.go Outdated
…ract

The Client interface previously said nothing about error semantics, so a
future consumer coded against tracker.Client alone had no forge-agnostic
way to detect "not found" without reaching into internal/forge directly
-- defeating the point of the abstraction. Add tracker.ErrNotFound /
IsNotFound, document that implementations must satisfy it, and have
ForgeClient translate forge.ErrNotFound into it via a small wrapNotFound
helper applied to all four methods.

Addresses review feedback from waynesun09 on PR #5993.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@waynesun09 waynesun09 added the ok-to-test Allow e2e CI to run after maintainer review (must be re-applied after each push) label Aug 7, 2026

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving

Clean, well-scoped interface + adapter. Re-reviewed the current state and verified independently — built, go vet-ed, and ran go test ./internal/tracker/... on the branch (Go 1.26): all green.

  • Prior review findings resolved: UpdateComment now carries number int (with a clear doc note that forge ignores it but Jira needs it), and the ErrNotFound / IsNotFound contract is defined and documented on the interface.
  • splitProject correctly last-/-splits so GitHub owner/repo and nested GitLab group/subgroup/project both work, and it rejects empty owner/repo rather than forwarding malformed values — covered by tests.
  • wrapNotFound uses double-%w so the returned error satisfies both tracker.ErrNotFound and the underlying forge.ErrNotFound, verified by TestForgeClient_GetIssue_NotFound.
  • Compile-time interface assertions (var _ Client = …) and TDD coverage across happy-path, nested-namespace, not-found, create/list, update, and invalid-ID.

One tiny optional nit, non-blocking: CreateComment dereferences the *forge.IssueComment without a nil check — only a theoretical panic if a forge.Client impl ever returned (nil, nil) on success, which none do today. Fine to leave.

LGTM.

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 7, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:55 PM UTC · Completed 3:12 PM UTC

Commit: fbcb35e · View workflow run →

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.76471% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/tracker/forge_client.go 87.69% 4 Missing and 4 partials ⚠️
internal/forge/fake.go 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 7, 2026

Copy link
Copy Markdown

Looks good to me

Previous run

Looks good to me

Previous run (2)

Review

Findings

High

  • [commit-convention-violation] PR title uses feat prefix for an internal abstraction with no user-facing impact. Per COMMITS.md, feat is reserved for end-user-facing functionality — "Adding internal packages, helpers, or abstractions that don’t change user-visible behavior → refactor." The PR body confirms this: "Nothing calls tracker.Client yet." GoReleaser uses PR titles for release notes, so this will incorrectly appear in the Features section.
    Remediation: Change PR title from feat(#5988): introduce tracker.Client interface with forge adapter to refactor(#5988): introduce tracker.Client interface with forge adapter.

Low

  • [scope-authorization-incomplete] internal/tracker/tracker.go:75 — The implemented UpdateComment signature adds a number int parameter not present in issue Introduce tracker.Client: a forge-agnostic issue-content interface for GitHub/GitLab #5988’s specification. The deviation is technically sound (Jira requires the issue key to update a comment, and the code documents this reason), but the PR body does not mention the signature change.

  • [architecture-documentation-gap] internal/tracker/tracker.go:1 — The PR introduces a new abstraction layer (tracker.Client) alongside forge.Client. An ADR documenting the tracker vs. forge boundary would strengthen the design for future contributors.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (3)

Review

Findings

High

  • [commit-convention-violation] PR title uses feat prefix for an internal abstraction with no user-facing impact. Per COMMITS.md, feat is reserved for end-user-facing functionality — "Adding internal packages, helpers, or abstractions that don't change user-visible behavior → refactor." The PR body confirms this: "Nothing calls tracker.Client yet." GoReleaser uses PR titles for release notes, so this will incorrectly appear in the Features section.
    Remediation: Change PR title from feat(#5988): introduce tracker.Client interface with forge adapter to refactor(#5988): introduce tracker.Client interface with forge adapter.

Low

  • [architecture-deviation-unapproved] internal/tracker/tracker.go:1 — The PR introduces a new abstraction layer (tracker.Client) alongside forge.Client. The design is authorized by issue Introduce tracker.Client: a forge-agnostic issue-content interface for GitHub/GitLab #5988 with clear rationale (Jira is not a forge, per 2026-07-30 team sync), and the existing internal/forge/jira/ package already demonstrates this separation in practice. An ADR documenting the tracker vs. forge boundary would strengthen the design for future contributors.

  • [doc-style] internal/tracker/forge_client.go:76 — ForgeClient.UpdateComment has a 4-line doc comment explaining its semantics, while the other three exported methods (GetIssue, ListComments, CreateComment) have only // <Method> implements Client. The interface definition in tracker.go shows the same pattern: UpdateComment has a 2-line comment, others have none. The UpdateComment comment exists because its semantics are unusual (the number param is unused for forge backends), which is worth documenting, but the inconsistency is notable.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (4)

Review

Findings

High

  • [commit-convention-violation] PR title uses feat prefix for an internal abstraction with no user-facing impact. Per COMMITS.md, feat is reserved for end-user-facing functionality — "Adding internal packages, helpers, or abstractions that don't change user-visible behavior → refactor." The PR body confirms this: "Nothing calls tracker.Client yet." GoReleaser uses PR titles for release notes, so this will incorrectly appear in the Features section.
    Remediation: Change PR title from feat(#5988): introduce tracker.Client interface with forge adapter to refactor(#5988): introduce tracker.Client interface with forge adapter.

Low

  • [documentation] internal/tracker/tracker.go:70 — The UpdateComment interface method doc comment uses a multi-line explanatory style (5 lines) that differs from the compact single-line comments on the other three interface methods (GetIssue, ListComments, CreateComment). The explanation of why number is part of the signature despite being redundant for some backends is valuable context; consider inlining it more concisely.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🤖 Finished Fix · ✅ Success · Started 3:13 PM UTC · Completed 3:19 PM UTC

Commit: fbcb35e · View workflow run →

Condense UpdateComment doc comment from 5 lines to 2 lines, matching the
compact style of the other Client interface methods while retaining the
key rationale (Jira requires the issue key to update a comment).

PR title convention (feat → refactor) noted but requires manual change
since sandbox cannot mutate the PR title.

Addresses review feedback on #5993
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

🔧 Fix agent — iteration 1 (bot-triggered)

Addressed both review findings. (1) PR title convention: verified feat should be refactor per COMMITS.md; sandbox cannot edit the PR title -- requires manual change by maintainer. (2) UpdateComment doc comment: condensed from 5 lines to 2 lines, matching the compact style of the other Client interface methods.

Fixed (2):

  1. PR title uses feat prefix for internal abstraction (commit-convention-violation): Verified finding: COMMITS.md says internal packages/abstractions with no user-visible behavior change should use refactor, not feat. PR title should change from 'feat(Introduce tracker.Client: a forge-agnostic issue-content interface for GitHub/GitLab #5988): introduce tracker.Client interface with forge adapter' to 'refactor(Introduce tracker.Client: a forge-agnostic issue-content interface for GitHub/GitLab #5988): introduce tracker.Client interface with forge adapter'. Sandbox cannot mutate the PR title (GraphQL blocked); this requires a manual title edit by a maintainer.
  2. UpdateComment doc comment inconsistent with other interface methods (documentation) (internal/tracker/tracker.go): Condensed the 5-line UpdateComment doc comment to 2 lines, matching the compact style of GetIssue, ListComments, and CreateComment. Preserved the key rationale: number is included because Jira requires the issue key to update a comment.

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 7, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:21 PM UTC · Completed 3:36 PM UTC

Commit: 082fee8 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🤖 Finished Fix · ✅ Success · Started 3:37 PM UTC · Completed 3:45 PM UTC

Commit: 082fee8 · View workflow run →

Add doc comments to GetIssue, ListComments, and CreateComment in the
Client interface and ForgeClient methods to match the existing
UpdateComment documentation style.

Addresses review feedback on #5993
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

🔧 Fix agent — iteration 2 (bot-triggered)

Addressed 3 of 4 findings. Fixed doc-style inconsistency by adding doc comments to undocumented interface methods and expanding ForgeClient method comments. PR title change (feat->refactor) was attempted but blocked by sandbox network policy -- requires manual change. Disagreed with ADR suggestion as out of scope.

Fixed (3):

  1. commit-convention-violation: PR title uses feat instead of refactor: Attempted to change PR title from feat(Introduce tracker.Client: a forge-agnostic issue-content interface for GitHub/GitLab #5988) to refactor(Introduce tracker.Client: a forge-agnostic issue-content interface for GitHub/GitLab #5988) via gh pr edit, but the sandbox network policy blocks GraphQL mutations. The PR title must be changed manually or by a post-script with appropriate permissions: change to 'refactor(Introduce tracker.Client: a forge-agnostic issue-content interface for GitHub/GitLab #5988): introduce tracker.Client interface with forge adapter'.
  2. doc-style: inconsistent doc comments across Client interface and ForgeClient methods (internal/tracker/tracker.go): Added one-line doc comments to GetIssue, ListComments, and CreateComment in the Client interface to match UpdateComment's documented style.
  3. doc-style: ForgeClient method comments inconsistent with UpdateComment (internal/tracker/forge_client.go): Expanded GetIssue, ListComments, and CreateComment doc comments from bare 'implements Client' to include a brief description of the adapter behavior, matching UpdateComment's explanatory style.

Disagreed (1):

  1. architecture-deviation-unapproved: suggest adding an ADR for tracker vs forge boundary: The reviewer acknowledges the design is authorized by issue Introduce tracker.Client: a forge-agnostic issue-content interface for GitHub/GitLab #5988 with clear rationale. An ADR is a separate deliverable that should be tracked as its own issue, not addressed as part of a fix iteration on this PR. The current PR is a pure interface + adapter with no callers yet, so the ADR can be written alongside or after the first consumer lands.

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 7, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:46 PM UTC · Completed 4:01 PM UTC

Commit: bed9407 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review-only pass focused on two Medium findings not covered by existing threads on this PR: duplicated owner/repo-splitting logic between internal/tracker and internal/poll, and a NotFound-contract test-coverage gap across three of Client's four methods.

Comment thread internal/tracker/forge_client.go
Comment thread internal/tracker/tracker_test.go
The comment claimed GitLab comment IDs are globally unique like GitHub's,
but GitLab's Notes API actually requires the issue/MR IID to address a
note directly (see gitlab.LiveClient.updateOrDeleteNote's scan-based
workaround). forge.Client's UpdateIssueComment doesn't expose that IID,
so ForgeClient's GitLab path still hits the scan; the doc now says so
instead of implying GitLab doesn't need the number at all.

Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
…racker NotFound tests

FakeClient.UpdateIssueComment returned nil for an unknown comment ID
instead of forge.ErrNotFound, which meant nothing exercised
ForgeClient.UpdateComment's wrapNotFound path through the fake. Also add
the missing NotFound-contract tests for ListComments, CreateComment, and
UpdateComment (only GetIssue had one).

Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@ralphbean ralphbean changed the title feat(#5988): introduce tracker.Client interface with forge adapter refactor(#5988): introduce tracker.Client interface with forge adapter Aug 10, 2026
@ralphbean

Copy link
Copy Markdown
Member Author

Re: #5993 (comment) — the feat-vs-refactor point is fair, this is an internal abstraction with no user-visible behavior yet. Retitled to refactor(#5988): ....

The other two items in there (UpdateComment's number param not being in #5988's spec, and the ADR suggestion) are covered by the separate inline review threads on this PR, so I'll reply to those there rather than duplicating it here.

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:04 PM UTC · Completed 3:18 PM UTC

Commit: 30a739b · View workflow run →

@fullsend-ai-review
fullsend-ai-review Bot dismissed stale reviews from themself August 10, 2026 15:18

Superseded by updated review

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Aug 10, 2026
@ralphbean
ralphbean added this pull request to the merge queue Aug 10, 2026

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review sweep — 2 additional findings not covered by existing review threads.

Comment thread internal/tracker/forge_client.go Outdated
Comment thread internal/tracker/tracker.go Outdated
@ralphbean
ralphbean removed this pull request from the merge queue due to a manual request Aug 10, 2026
forge.ErrNotFound's own text is "not found", so wrapping it with
fmt.Errorf("%w: %w", ErrNotFound, err) produced doubled text like
"not found: not found" or "not found: not found: comment 42" whenever
a ForgeClient method's Error() was rendered directly (CLI output, logs).
errors.Is/IsNotFound checks were unaffected, but the surfaced message
was wrong.

notFoundError wraps the forge error without repeating the sentinel
text — its Error() returns the forge error's message verbatim, while
Unwrap() []error still satisfies both tracker.IsNotFound and
forge.IsNotFound.

Reported by waynesun09 on PR #5993.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
waynesun09 flagged that a plain string Body doesn't account for Jira's
ADF requirement — internal/forge/jira already models bodies as `any`
for exactly this reason.

Confirmed with a live spike against Jira's v3 API: posting a bare
string as a comment body is rejected outright ("Comment body is not
valid!"). Naively wrapping the raw Markdown string in a single ADF
text node is worse than lossy — Jira's plain-text rendering path
interprets stray Markdown characters as wiki-markup, so braces in a Go
code sample broke the surrounding paragraph and a Markdown link got
mangled into a dead in-page anchor. A properly structured ADF payload
(codeBlock node, link mark, bulletList) rendered correctly.

tracker.Body documents this contract so a future Jira Client
implementation is responsible for real Markdown<->ADF conversion,
rather than callers or the interface silently assuming a pass-through
works.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 4:08 PM UTC · Completed 4:21 PM UTC

Commit: 4ebdbea · View workflow run →

@ralphbean
ralphbean enabled auto-merge August 10, 2026 16:14
@ralphbean
ralphbean added this pull request to the merge queue Aug 10, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 10, 2026

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review squad follow-up: one additional Medium finding not covered by existing threads on this PR.

Comment thread internal/forge/fake.go
@ralphbean
ralphbean added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit 1d189a9 Aug 10, 2026
27 of 29 checks passed
@ralphbean
ralphbean deleted the agent/5988-tracker-client branch August 10, 2026 17:39
@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 10, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 5:41 PM UTC · Completed 6:10 PM UTC

Commit: 4ebdbea · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #5993 — tracker.Client interface with forge adapter

PR: #5993refactor(#5988): introduce tracker.Client interface with forge adapter
Author: ralphbean (human-authored, 11 commits)
Review: 4 human review passes by waynesun09, 5 review agent runs, 3 fix agent iterations
Merged: 2026-08-10 after 4 days

What went well

  • Challenger subagent worked as designed: In review run 4 (run 31401552509), all 5 style-conventions findings were false positives (claimed docs were missing when they existed in the source). The challenger correctly removed all 5, preventing spurious review feedback.
  • qodo-code-review caught a real bug: The splitProject validation gap (accepting malformed inputs like /repo or owner/) was found early and fixed in commit 70a15d6.
  • Commit convention finding: The review agent correctly flagged featrefactor for an internal abstraction with no user-visible impact.
  • Responsive author: ralphbean addressed all feedback promptly, including non-trivial fixes (defining tracker.ErrNotFound, adding a notFoundError type to avoid message stuttering, and spiking Jira ADF compatibility).

What the review agent missed

The human reviewer (waynesun09) found 8 substantive findings across 4 review passes. The review agent missed all of the interface design and adapter pattern findings despite extensive exploration (the correctness subagent ran 12 Grep searches and multiple file reads across the forge package in run 4 alone):

  1. Error message stuttering (MEDIUM): wrapNotFound wrapping forge.ErrNotFound (text: "not found") with tracker.ErrNotFound (text: "not found") produced "not found: not found". The correctness subagent read the wrapNotFound code but didn't trace the error message chain end-to-end.
  2. Jira ADF body type (MEDIUM): Body as plain string doesn't account for Jira's Atlassian Document Format. The agent read jira/types.go but didn't connect this to the tracker type design.
  3. Duplicate splitProject logic (MEDIUM): Nearly identical owner/repo splitting already existed in internal/poll/poll.go. Author filed #6043.
  4. NotFound test coverage gap (MEDIUM): NotFound contract tested for only 1 of 4 Client methods, and FakeClient.UpdateIssueComment returned nil instead of ErrNotFound for unknown IDs, masking the gap.
  5. UpdateComment missing number param (HIGH): Jira's REST API needs the issue key to update a comment. Fixed early in commit 690c9a6.
  6. No ErrNotFound sentinel (HIGH): Consumers of tracker.Client had no forge-agnostic way to detect "not found." Fixed in commit ebd1662.

Review-fix loop (3 iterations, Aug 7)

The review-fix loop ran 3 fix iterations over ~1.5 hours. The review agent found a valid HIGH finding (commit convention) but the fix agent couldn't change the PR title due to sandbox network restrictions (403 on gh pr edit). The loop repeated because the review agent rediscovered the unfixed title on each re-review. The fix agent recognized the sandbox limitation on iteration 3 and stopped retrying. This consumed ~30M review + fix tokens for an issue requiring human intervention.

Existing issues cover this pattern: #902 (circuit breaker), #2959 (dedup findings across iterations), #2418 (fix agent PR metadata), #685 (persist prior findings on re-review).

Redundant dispatches

30 shim runs fired on this PR branch: 5 dispatched review, 3 dispatched fix, 1 dispatched retro, 21 were no-ops (pull_request_review events matching no dispatch stage, or cancelled by concurrency groups). Heavily covered by 20+ existing issues (#893, #963, #1271, #2994, #4681, #5967).

Existing issues covering other observations

  • Duplicate logic detection (splitProject): covered by agents#333 and agents#322.
  • Cross-method interface implications: partially covered by agents#315.
  • Error shape enumeration: related to agents#724.
  • Review agent incorporating human reviews: agents#447.

Autonomy readiness

The review agent is not ready for increased autonomy on PRs introducing new Go interface adapters. The human reviewer caught 6+ findings the agent missed, including error wrapping quality, backend-specific type design, cross-package duplication, and test coverage gaps. The agent's unique contributions beyond what the human found were LOW-severity procedural items (doc formatting, ADR suggestion). Human review remains essential for this class of change.

Proposals filed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fullsend-fix Enables automatic bot-triggered fix runs on human-authored PRs ok-to-test Allow e2e CI to run after maintainer review (must be re-applied after each push) ready-for-merge All reviewers approved — ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Introduce tracker.Client: a forge-agnostic issue-content interface for GitHub/GitLab

2 participants