3 bugs found and fixed: - Workflow if: expression parser rejects # comments - Forgejo doesn't populate github.event.action in if: context - Forgejo sends 'label_updated' instead of 'labeled' Key learning: normalize Forgejo event action names in context.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 KiB
PRD #6: E2E Test All Untested Forgejo Trigger Scenarios
Issue: #6 Priority: High Created: 2026-02-25
Problem
The Forgejo platform support (PRD #1) was validated for the two most common scenarios (agent mode via workflow_dispatch and tag mode via issue_comment). However, 5 trigger scenarios remain untested in a live environment:
- PR review comment triggers (
pull_request_review_comment) - PR review triggers (
pull_request_review) - Label triggers (
issueswithlabeledaction) - Assignee triggers (
issueswithassignedaction) - Branch creation + code changes from tag mode (the initial test only asked a question, no code was generated)
These untested paths could have silent failures — the code may look correct but break on Forgejo's subtly different webhook payloads, API responses, or event structures.
Solution
Systematically exercise each untested scenario on the live Forgejo instance (git.wxs.ro), document results, fix any issues discovered, and update the E2E status table in docs/forgejo.md.
Milestones
-
M1: Ensure workflow covers all trigger types
- Review
.forgejo/workflows/claude.ymlto confirm all 5 trigger types are listed - Add any missing triggers to the workflow file
- Ensure the workflow has the correct permissions for each trigger type
- Validation: Workflow YAML includes all required
on:triggers
- Review
-
M1.5: Fix image-downloader test failures blocking CI
- Problem: All 18
downloadCommentImagestests failed on Forgejo CI (Linux Bun 1.2.21 inharbor.wxs.ro/wxs/grunt-runner:v1.9.0) while passing locally on macOS. This blocked all PRs. - Root cause:
mock.module("../src/github/utils/image-downloader")intest/forgejo-data-fetcher.test.tsreplaced the image-downloader module globally in Bun's module registry for the entire process. When Bun's non-deterministic test file ordering ran forgejo-data-fetcher.test.ts BEFORE image-downloader.test.ts, the latter imported the stubbed module instead of the real one, breaking all mock tracking. The failure was intermittent because file ordering changed when other files were added/removed. - Fix (2 parts):
c6ca8a7: Added DI (FsDepsinterface + optionalfsDepsparam) todownloadCommentImages()— removedmock.module("fs/promises")from the image-downloader test itself49eedd5: Removed all image-downloader mocking from forgejo-data-fetcher.test.ts entirely — the real function is safe to call because test comments have no GitHub image URLs, so it returns an empty Map naturally. Bothmock.moduleandspyOncaused cross-file pollution in Bun's single-process runner.
- Key learning: Never use
mock.module()in Bun test files — it poisons the module registry for all files in the process.spyOnon ESM namespace exports also fails to restore correctly across files on Linux Bun. Best approach: avoid mocking entirely when the real function is safe to call. - Validation: CI passed (all 3 jobs: test, prettier, typecheck) on commit
49eedd5
- Problem: All 18
-
M2: Test PR review comment trigger
- Create a test PR on the repo ✓ (PR #15)
- Post a review comment with
@claudemention ✓ (review comments ontest/e2e-prd6-m2.txt) - Verify: platform detection, context fetching (PR diff, review comment body), tracking comment, Claude response ✓
- Document any issues and fix them ✓ (3 bugs found and fixed, see below)
- Validation: Claude responds correctly to
@claudein a PR review comment ✓ (run #12075, 32s, full PR summary posted) - Bugs found & fixed:
9d8c96f–ac8e22e: Forgejopull_request_review_commentpayload lacks the top-levelcommentobject — added fallback to issue comment creation and 404 fallback for review comment fetche73c881: Forgejo sends event namepull_request_comment(notpull_request_review_comment) — skippedcommentBodyvalidation for this eventc3c3eb5: Critical — MCP comment server setisPullRequestReviewComment=trueon Forgejo, causing every Claude update to trypulls.updateReviewComment(404) and silently fail. Fix: detect Forgejo viaGITHUB_API_URLand route to issue comment API. Also hardenedupdate-claude-comment.tsfallback to catch 422 alongside 404, and skipped the wastedpulls.getReviewCommentcall inupdate-comment-link.tson Forgejo.
- Key learning: On Forgejo, the tracking comment for review comment events is always an issue comment (not a PR review comment reply) because the payload lacks
comment.id. All update paths must account for this.
-
M3: Test PR review trigger
- Reused test PR #15 from M2 ✓
- Submitted reviews with
@claudemention in the review body ✓ (review IDs 20, 21, 22) - Verify: platform detection, context fetching, tracking comment, Claude response ✓
- Document any issues and fix them ✓ (1 finding, 1 fix, see below)
- Validation: Claude responds correctly to
@claudein a PR review ✓ (runs 12080, 12087) - Finding: Forgejo sends
pull_request_commentinstead ofpull_request_reviewfor review submissions — same pattern as M2. Thepull_request_reviewtrigger and workflow condition (line 21) are dead code on Forgejo. - Fix (
e514bf5→b21dbd6): Replaced blind Forgejo auto-trigger intrigger.tswith payload-aware@claudecheck that inspectscomment.bodyandreview.bodywith fallback. Attempted workflowif:gating but reverted — Forgejo'spull_request_commentpayload does not populategithub.event.comment.bodyorgithub.event.review.body(run 12084 was skipped). - Key learning: On Forgejo, the workflow
if:condition cannot reliably gate on payload body fields forpull_request_commentevents. The@claudecheck must happen in application code (trigger.ts), not in the workflow condition.
-
M4: Test label trigger
- Created
claudelabel (id: 125) on the repo ✓ - Applied label to test issues (#17, #18, #19) ✓
- Verify: trigger detection, context fetching (issue body/title), Claude response ✓
- Document any issues and fix them ✓ (3 bugs found and fixed, see below)
- Validation: Claude responds when the configured label is applied to an issue ✓ (run 52795, 20s, 2 turns, $0.12)
- Also verified: creating issue with label in one go fires exactly 1 run (issue #19)
- Bugs found & fixed:
a864b16: Workflowif:expression parser rejects#comments — Forgejo runner's expression lexer treats#as invalid token. Moved all comments outside theif:block, switched from|to>-scalar.414c705→a864b16: Forgejo does not populategithub.event.actionin workflowif:context forissuesevents (run 12061 skipped despite action beinglabeled). Let allissuesevents through the gate;trigger.tsvalidates.baa007a: Forgejo sendslabel_updatedinstead oflabeledaseventActionfor issue label events. AddedFORGEJO_EVENT_ACTION_MAPincontext.tsto normalize at parse time (same pattern as event name normalization).
- Key learning: Forgejo's
issuesevent action names differ from GitHub —label_updatednotlabeled. Normalize incontext.tsat parse time so all downstream code works unchanged. Creating an issue with a label fires bothopenedandlabel_updatedas separate events — do NOT check labels onopenedto avoid duplicate runs.
- Created
-
M5: Test assignee trigger
- Assign a user to an issue to trigger the action
- Verify: trigger detection, context fetching, Claude response
- Document any issues and fix them
- Validation: Claude responds when assigned to an issue
-
M6: Test tag mode with code changes (branch creation)
- Create an issue requesting a code change (e.g., "add a comment to file X")
- Trigger Claude via
@claudemention - Verify: Claude creates a branch, pushes commits, links the branch in tracking comment
- Verify the Forgejo-specific PR compare URL format is correct
- Validation: Claude successfully creates a branch with code changes on Forgejo
-
M7: Update documentation with E2E results
- Update
docs/forgejo.mdE2E status table with results for all 5 scenarios - Move tested items from "Not Yet E2E Tested" to the validated table
- Document any Forgejo-specific quirks or limitations discovered
- Add troubleshooting entries for any issues encountered
- Validation:
docs/forgejo.mdaccurately reflects the current E2E validation status
- Update
Dependencies
- PRD #5 (Dedicated Forgejo User) is recommended but not strictly required — tests can run with the current token setup
- A test repository on git.wxs.ro with the action workflow configured
Risks
| Risk | Mitigation |
|---|---|
| Forgejo webhook payloads differ from expected | Log raw payloads, compare with GitHub format, adapt parsing |
| Trigger type not supported by Forgejo Actions | Document as known limitation, skip gracefully |
| Action creates unwanted branches/comments during testing | Use a dedicated test repo or clean up after each test |
| Bun mock.module breaks mock tracking on Linux | RESOLVED: use spyOn on module namespace, never mock.module |
| MCP comment updates silently fail on Forgejo | RESOLVED: route to issue comment API when platform=forgejo |
| Workflow if: cannot gate on payload body for Forgejo | RESOLVED: @claude check moved to trigger.ts application code |
| Workflow if: expression parser rejects # comments | RESOLVED: moved comments outside if: block, use >- scalar |
| Forgejo event action names differ from GitHub | RESOLVED: FORGEJO_EVENT_ACTION_MAP normalizes at parse time |
| Creating issue with label fires duplicate runs | RESOLVED: label check only on labeled event, not on opened |
Success Criteria
- All 5 previously untested scenarios have been exercised on a live Forgejo instance
- Any bugs discovered are fixed and tested
docs/forgejo.mdE2E status table is fully up-to-date- "Not Yet E2E Tested" section is empty (all items moved to validated or documented as unsupported)