feat: tag swamp-originated lab issues with source="swamp"#1140
Conversation
swamp-club#375 added a `source` field to lab issues so each one can be attributed to its origin (web app, CLI, extensions, UAT). Both paths in this repo that create lab issues — the CLI's `swamp issue` command and the GitHub auto-responder workflow — were defaulting to "swamp-club". Tag both with `source: "swamp"`. The auto-responder also switches from `/api/v1/lab/issues/ensure` to the public `/api/v1/lab/issues` endpoint, since `/ensure` doesn't accept the `source` field. The footer is expanded to include the full GitHub issue URL so the back-link survives the loss of the structured githubRepoFullName/githubIssueNumber fields. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
Clean, well-scoped PR. The source: "swamp" field is correctly added in both paths (CLI client and GitHub workflow), and the explicit field listing in submitIssue prevents any unexpected properties from leaking into the payload.
Blocking Issues
None.
Suggestions
None — the tests cover both success and failure paths, the workflow changes are consistent, and the handler type widening (Response | Promise<Response>) in the test helper is a nice touch to support the async mock.
There was a problem hiding this comment.
CI Security Review
Critical / High
None.
Medium
None.
Low
None.
Analysis
Prompt Injection — Not applicable. No LLM is invoked in this workflow.
Expression Injection — Safe. The workflow uses actions/github-script where issue data (issue.title, issue.body, issue.html_url) is accessed via JavaScript variables and serialized through JSON.stringify(). No ${{ }} interpolation of untrusted data in run: blocks or YAML contexts.
Dangerous Triggers — issues: [opened] is triggered by external users, but the workflow only reads issue metadata and forwards it to an external API via properly serialized JSON. No code checkout, no code execution from issue content.
Supply Chain — actions/github-script@v7 is a GitHub-owned action, acceptable with tag-only pin per policy.
Permissions — issues: write and contents: read at workflow level. Single-job workflow, so workflow-level scoping is equivalent to job-level. Permissions are minimal for the task (comment, close issue, read content).
Secret Exposure — SWAMP_CLUB_API_KEY is accessed via process.env, used only in the Authorization header of a fetch call. Not logged or interpolated into shell commands. Error messages do not leak the secret.
Auto-merge — Not applicable; this workflow does not merge anything.
Verdict
PASS — The changes are security-neutral. The endpoint switch from /ensure to /issues, the addition of source: "swamp", and the footer format change introduce no new attack surface. All untrusted issue data continues to flow through JSON.stringify() serialization.
There was a problem hiding this comment.
Adversarial Review
Critical / High
None.
Medium
.github/workflows/auto-response.yml:43-57— Loss of idempotency creates duplicate risk on workflow re-runs.
The old/ensureendpoint was idempotent on(repo, issueNumber). The new/issuesendpoint is not. If the workflow succeeds at creating the lab issue (line 43) but then fails atcreateComment(line 93) orissues.update(line 102) — e.g. due to a transient GitHub API error — and an admin manually re-runs the workflow, a second lab issue will be created. The PR notes acknowledge this and argue thatissues.openedis one-shot, which is true for automatic triggers, but manual re-runs bypass that.
Breaking example: Lab issue created →createCommenttimes out → admin clicks "Re-run all jobs" → duplicate lab issue with duplicate footer.
Suggested fix: Accept as-is if duplicates are tolerable, or add a comment in the workflow warning maintainers not to re-run the job (since the lab issue may already exist). Alternatively, check for an existing comment from the bot before creating a new lab issue.
Low
-
.github/workflows/auto-response.yml:54—typeis hardcoded to"feature"for all GitHub issues. Bug reports opened on GitHub will be tagged as features in the Lab. This is pre-existing behavior (not introduced by this PR), but worth noting since the PR is already touching this payload — it would be a one-line fix to attempt to infer the type from GitHub labels. -
src/infrastructure/http/swamp_club_client.ts:180-185— Explicit field enumeration is good but brittle if the input type grows. If a new field is added to theinputparameter type in the future, a developer could add it to the TypeScript type but forget to add it to theJSON.stringifyobject literal. The oldJSON.stringify(input)pattern would have forwarded it automatically. This is a minor maintainability concern, not a bug — the explicit enumeration is arguably safer since it prevents accidental field leakage.
Verdict
PASS — Clean, well-scoped change. The idempotency trade-off is acknowledged and acceptable given the one-shot trigger semantics. Tests cover the new behavior. No security, correctness, or resource management issues found.
Summary
swamp-club#375 added a
sourcefield on lab issues so each one is attributed to its origin (swamp-club,swamp,extensions,UAT). Both paths in this repo that create lab issues defaulted toswamp-club, which is wrong. Tag both withsource: "swamp"so the Lab UI shows the correct chip and filter bucket.SwampClubClient.submitIssue(used byswamp issue bug | feature | security) now sendssource: "swamp"as a top-level field on the JSON payload alongsidetype,title, andbody..github/workflows/auto-response.ymlswitches from the admin-only/api/v1/lab/issues/ensureendpoint (which doesn't acceptsource) to the public/api/v1/lab/issuesendpoint, and tags submissions withsource: "swamp". The footer is expanded to include the full GitHub issue URL (issue.html_url) so the back-link survives the loss of the structuredgithubRepoFullName/githubIssueNumberfields that the public endpoint doesn't accept.The extension lifecycle client (
extensions/models/_lib/swamp_club.ts) was refactored in #1139 to operate only on existing issues by number — it no longer creates lab issues, so nothing to update there.Notes
/issuesendpoint accepts the sameBearer ${SWAMP_CLUB_API_KEY}header as/ensure, confirmed via swamp-club'sroutes/_middleware.tswhich recognizesBearer swamp_…API keys for any/api/*route./issuesis not idempotent on(repo, issueNumber)like/ensurewas, butauto-response.ymlonly fires onissues.opened(one-shot per GitHub issue), so dedup behavior is unchanged in practice.MAX_TITLE_LENGTHis 256 chars; GitHub titles can exceed this. The current workflow doesn't truncate — same as before. Failures keep the GitHub issue open viacore.setFailed.Test plan
deno fmt— cleandeno lint— cleandeno check— cleandeno run test— 4204 passed, 0 failed (includes 2 newsubmitIssuetests that capture the actual HTTP request body and assertcapturedBody.source === "swamp")deno run compile— recompiledswamp issue bug --title "source smoke" --body "verify source=swamp" --json, then open the resulting issue in the swamp.club UI and confirm the "Swamp CLI" chip appearssysteminit/swampand verify the auto-responder produces a lab issue with the "Swamp CLI" chip and a footer linking back to the GitHub URL🤖 Generated with Claude Code