Skip to content

refactor: drop GitHub integration from issue-lifecycle, use swamp-club directly#1139

Merged
stack72 merged 3 commits intomainfrom
worktree-shimmering-percolating-seal
Apr 8, 2026
Merged

refactor: drop GitHub integration from issue-lifecycle, use swamp-club directly#1139
stack72 merged 3 commits intomainfrom
worktree-shimmering-percolating-seal

Conversation

@stack72
Copy link
Copy Markdown
Contributor

@stack72 stack72 commented Apr 8, 2026

Summary

The @swamp/issue-lifecycle extension and skill now operate directly on swamp-club lab issues. Issues must already exist in swamp-club — the model fetches them by sequential lab number via GET /api/v1/lab/issues/{number} and PATCHes the type field during triage so the classification is reflected back in swamp-club.

  • Deleted extensions/models/_lib/issue_tracker.ts (387 lines of gh CLI wrapper) and every tracker/GitHub call in issue_lifecycle.ts.
  • Global args changed: repo is dropped; issueNumber now refers to a swamp-club lab issue number. Version bumped to 2026.04.08.1 with an upgrade entry that strips repo from old instances.
  • SwampClubClient rewritten to operate on the lab number directly. New fetchIssue() (GET) and updateType() (PATCH) methods. The GitHub-coupled /ensure round-trip is gone.
  • Classification types reconciled to swamp-club's bug | feature | security. Regressions are modelled as type: bug with a new isRegression: boolean flag on the classification record. unclear is gone — use confidence: low + clarifyingQuestions instead.
  • Methods removed: ci_status, record_pr, fix (all GitHub-PR/CI coupled). Resources removed: ciResults, fixDirective. Phase removed: ci_review. The state machine is now implementing → done directly.
  • Skill docs rewritten (SKILL.md, triage.md, implementation.md) and the extension README.md updated for the swamp-club-first workflow.

Depends on systeminit/swamp-club#374 (already merged), which adds PATCH-type support to swamp-club. Without that PR the triage method's type update would no-op with a 400.

New state machine

created → triaging → classified → plan_generated → approved → implementing → done
                                        ↑              ↑
                                        └─ iterate ────┘
                                        (with adversarial_review, resolve_findings)

Diff stats

9 files changed, 375 insertions(+), 1422 deletions(-) — net −1047 lines.

Test plan

  • deno check — clean
  • deno lint — 1007 files, clean
  • deno fmt — 1113 files formatted
  • deno run test4193 passed, 0 failed
  • deno run compile — binary rebuilt
  • Smoke test against a real swamp-club issue: create issue #N in the swamp-club UI, run swamp model create @swamp/issue-lifecycle issue-<N> --global-arg issueNumber=<N>, then starttriage and verify the swamp-club issue type flips and the lifecycle entries appear.

🤖 Generated with Claude Code

…b directly

The issue-lifecycle extension and skill now operate directly on swamp-club
lab issues. Issues must already exist in swamp-club — the model fetches them
by sequential lab number via GET /api/v1/lab/issues/{number} and PATCHes the
type during triage to reflect the classification back.

- Delete extensions/models/_lib/issue_tracker.ts and every gh CLI call.
- Swap GlobalArgs: drop repo, issueNumber now refers to a swamp-club lab
  issue. Version bumped to 2026.04.08.1 with an upgrade that strips repo.
- Rework SwampClubClient: operate on the lab number directly, add
  fetchIssue() and updateType(). Drop the /ensure round-trip.
- Reconcile classification types to swamp-club's bug/feature/security.
  Regressions become type=bug with an isRegression flag on the
  classification record. unclear is gone — use confidence=low +
  clarifyingQuestions instead.
- Remove ci_status, record_pr, fix methods and ciResults/fixDirective
  resources. The ci_review phase is gone; implementing transitions
  straight to done.
- Rewrite skill SKILL.md, triage.md, implementation.md and the extension
  README for the swamp-club-first workflow.

Depends on swamp-club PATCH-type support in systeminit/swamp-club#374.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
stack72 added a commit to systeminit/swamp-extensions that referenced this pull request Apr 8, 2026
…b directly

Mirrors the changes in systeminit/swamp#1139 — the @swamp/issue-lifecycle
extension and skill now operate directly on swamp-club lab issues. Issues
must already exist in swamp-club; the model fetches them by sequential lab
number via GET /api/v1/lab/issues/{number} and PATCHes the type during
triage to reflect the classification back.

- Delete extensions/models/_lib/issue_tracker.ts and every gh CLI call.
- Swap GlobalArgs: drop repo, issueNumber now refers to a swamp-club lab
  issue. Version bumped to 2026.04.08.1 with an upgrade that strips repo.
- Rework SwampClubClient: operate on the lab number directly, add
  fetchIssue() and updateType(). Drop the /ensure round-trip.
- Reconcile classification types to swamp-club's bug/feature/security.
  Regressions become type=bug with an isRegression flag on the
  classification record. unclear is gone — use confidence=low +
  clarifyingQuestions instead.
- Remove ci_status, record_pr, fix methods and ciResults/fixDirective
  resources. The ci_review phase is gone; implementing transitions
  straight to done.
- Update schemas_test.ts state machine tests to the new topology:
  happy path ends implementing→done, new completion gate test, old fix
  loop and ci_review tests removed.
- Rewrite skill SKILL.md, triage.md, implementation.md, extension
  README.md, and manifest.yaml description/method list for the
  swamp-club-first workflow.

Depends on swamp-club PATCH-type support in systeminit/swamp-club#374
(merged).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
github-actions[bot]

This comment was marked as outdated.

github-actions[bot]

This comment was marked as outdated.

The _swampClub module-level singleton was keyed on nothing — once created
for issue #N, every subsequent getSwampClub() call returned the same
client regardless of the new issueNumber in globalArgs. Since user models
are loaded via dynamic import() in the same process, the module stays
cached across method calls, so running start for issue #10 and then
issue #20 in the same session silently sent #20's lifecycle entries,
type updates, and status transitions to #10.

Drop the cache entirely and call createSwampClubClient directly at each
use. The reachability check is a single 5s-timeout HTTP GET and runs
once per method invocation — negligible next to the lifecycle POST
already happening on the same code path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
github-actions[bot]

This comment was marked as outdated.

github-actions[bot]

This comment was marked as outdated.

The swamp-club refactor accidentally dropped the final two lines of the
required copyright header ("You should have received a copy..." and
"with Swamp. If not, see..."). Per CLAUDE.md, every .ts file must carry
the full AGPLv3 header from FILE-LICENSE-TEMPLATE.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Adversarial Review

Critical / High

None found.

Medium

  1. extensions/models/issue_lifecycle.ts:475-497triage silently skips swamp-club type update if server goes down between start and triage.
    The start method (line 351) enforces swamp-club availability and throws if it's unreachable. But triage (line 479) checks if (sc) and silently skips the updateType() and transitionStatus() calls if the client can't be created. Since createSwampClubClient does a health check every time, a temporary network blip between start and triage means the swamp-club issue type is never updated, yet the local classification is persisted and the phase advances to classified. The README states "swamp-club is the single source of truth" — this silent degradation contradicts that contract.
    Breaking scenario: User runs start (succeeds), server has a 30-second outage, user runs triage type=security — local state says classified with type: security, but swamp-club still shows the original type and status. No error is surfaced.
    Suggested fix: Either throw (like start does) to make swamp-club mandatory on every mutation, or at minimum log at warning level that the type update was skipped. The same pattern applies to plan, iterate, approve, implement, and complete — all silently degrade. This is a conscious design choice vs. the old optional integration, so flagging for the author to confirm the intent.

  2. extensions/models/issue_lifecycle.ts:1002-1010approve writes state to "approved" before reading the plan.
    At line 1002 the state is written to phase: "approved", then at line 1010 the plan is read. If readResource fails (corrupt data, disk error), the state is already committed as "approved" but the swamp-club lifecycle entry was never posted. The plan-exists check guards against a missing plan, but not against a read failure after the check passes. Reordering to read the plan first would be safer.

  3. extensions/models/issue_lifecycle.ts:876-974resolve_findings silently ignores unknown finding IDs.
    If the user passes findingId: "ADV-99" but only ADV-1 through ADV-3 exist, the resolution is silently dropped. The resolutionMap.get(f.id) at line 924 simply returns undefined for unknown IDs. The user sees "Resolved 1 finding(s)" in the log but no finding was actually resolved.
    Suggested fix: Track unmatched IDs from resolutionMap after the loop and warn or throw.

Low

  1. extensions/models/_lib/swamp_club.ts:247-308createSwampClubClient performs a health check on every call.
    Every method (triage, plan, approve, etc.) calls createSwampClubClient, which fetches /api/health before returning. Over a full lifecycle (start → triage → plan → adversarial_review → resolve_findings → approve → implement → complete = 8 methods), that's 8 extra HTTP round-trips purely for health checks. Consider caching the client or passing it through context.

  2. extensions/models/issue_lifecycle.ts:817-820adversarial_review records planVersion: 0 if plan resource is somehow absent.
    The transition check restricts this to plan_generated phase, so a plan should always exist. But the fallback to version 0 means a corrupted state would produce a review that can never match a real plan version, which the adversarial-review-clear check would correctly reject. Self-healing, but worth noting.

Verdict

PASS — This is a clean deletion-heavy refactor that removes ~1050 lines of GitHub/CI integration in favor of direct swamp-club operation. The state machine is simpler and correct. The medium findings are design-level concerns about silent degradation that the author should confirm are intentional, but none represent data loss or security issues.

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review

Clean, well-executed refactoring that removes ~1050 lines of GitHub integration code and simplifies the issue-lifecycle model to operate directly on swamp-club lab issues. The state machine simplification (implementing → done instead of the ci_review/fix loop) is a welcome reduction in complexity.

Blocking Issues

None.

Suggestions

  1. Inconsistent swamp-club error handling across methods: start correctly throws when swamp-club is unavailable (if (!sc) throw ...), but triage, plan, iterate, approve, implement, and complete all silently skip swamp-club updates when the client is null (if (sc) { ... }). Since swamp-club is now mandatory (not optional), a failed updateType() in triage means swamp-club's type field silently drifts from the local classification. Consider either throwing on null in all methods (matching start's behavior) or at least logging a warning when swamp-club calls are skipped in methods that mutate swamp-club state (like triage's updateType).

  2. createSwampClubClient called fresh per method: Each method calls createSwampClubClient() independently, which performs a health check (fetch to the base URL) every time. The old code cached this in a module-level _swampClub variable. Since each method run is a separate process (as noted in the old code's comments), this is functionally correct, but worth being aware of — each method invocation now makes 2+ HTTP calls to swamp-club before doing its real work (health check + the actual operation).

  3. Type cast in fetchIssue: (issue.type ?? "feature") as IssueType at swamp_club.ts:114 — if the API returns an unexpected type string, this cast silently accepts it. Consider using IssueType.safeParse() to validate and fall back explicitly, or at least log when the fallback is used.

@stack72 stack72 merged commit 2dce892 into main Apr 8, 2026
10 checks passed
@stack72 stack72 deleted the worktree-shimmering-percolating-seal branch April 8, 2026 00:41
stack72 added a commit to systeminit/swamp-extensions that referenced this pull request Apr 8, 2026
…b directly (#57)

* refactor: drop GitHub integration from issue-lifecycle, use swamp-club directly

Mirrors the changes in systeminit/swamp#1139 — the @swamp/issue-lifecycle
extension and skill now operate directly on swamp-club lab issues. Issues
must already exist in swamp-club; the model fetches them by sequential lab
number via GET /api/v1/lab/issues/{number} and PATCHes the type during
triage to reflect the classification back.

- Delete extensions/models/_lib/issue_tracker.ts and every gh CLI call.
- Swap GlobalArgs: drop repo, issueNumber now refers to a swamp-club lab
  issue. Version bumped to 2026.04.08.1 with an upgrade that strips repo.
- Rework SwampClubClient: operate on the lab number directly, add
  fetchIssue() and updateType(). Drop the /ensure round-trip.
- Reconcile classification types to swamp-club's bug/feature/security.
  Regressions become type=bug with an isRegression flag on the
  classification record. unclear is gone — use confidence=low +
  clarifyingQuestions instead.
- Remove ci_status, record_pr, fix methods and ciResults/fixDirective
  resources. The ci_review phase is gone; implementing transitions
  straight to done.
- Update schemas_test.ts state machine tests to the new topology:
  happy path ends implementing→done, new completion gate test, old fix
  loop and ci_review tests removed.
- Rewrite skill SKILL.md, triage.md, implementation.md, extension
  README.md, and manifest.yaml description/method list for the
  swamp-club-first workflow.

Depends on swamp-club PATCH-type support in systeminit/swamp-club#374
(merged).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove module-level SwampClubClient cache

The _swampClub module-level singleton was keyed on nothing — once created
for issue #N, every subsequent getSwampClub() call returned the same
client regardless of the new issueNumber in globalArgs. Since user models
are loaded via dynamic import() in the same process, the module stays
cached across method calls, so running start for issue #10 and then
issue #20 in the same session silently sent #20's lifecycle entries,
type updates, and status transitions to #10.

Drop the cache entirely and call createSwampClubClient directly at each
use. The reachability check is a single 5s-timeout HTTP GET and runs
once per method invocation — negligible next to the lifecycle POST
already happening on the same code path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: restore truncated AGPLv3 header in swamp_club.ts

The swamp-club refactor accidentally dropped the final two lines of the
required copyright header ("You should have received a copy..." and
"with Swamp. If not, see..."). Per CLAUDE.md, every .ts file must carry
the full AGPLv3 header from FILE-LICENSE-TEMPLATE.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant