Skip to content

feat(gastown+web): slingExistingPr + BeadPanel babysit badge (chunks 0+4)#3536

Closed
jrf0110 wants to merge 5 commits into
convoy/babysit-existing-pr-feature/60d71828/headfrom
convoy/babysit-existing-pr-feature/60d71828/gt/toast/bc85648a
Closed

feat(gastown+web): slingExistingPr + BeadPanel babysit badge (chunks 0+4)#3536
jrf0110 wants to merge 5 commits into
convoy/babysit-existing-pr-feature/60d71828/headfrom
convoy/babysit-existing-pr-feature/60d71828/gt/toast/bc85648a

Conversation

@jrf0110

@jrf0110 jrf0110 commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Chunk 0 — slingExistingPr Town DO method + review-queue refactor:

  • Refactor submitToReviewQueue to use private createMergeRequestBeadCore helper (public signature unchanged)
  • Add submitExternalPrToReviewQueue function that creates merge_request bead + review_metadata without tracks edge, with gt:babysit label and babysit metadata
  • Add slingExistingPr method to Town.do.ts that validates repo match, checks PR state, fetches branches from SCM, and calls submitExternalPrToReviewQueue
  • Extend checkPRStatus return shape with head_branch, base_branch, head_sha, title fields (both GitHub and GitLab paths)
  • Add parsePrUrlForRepoMatch utility for repo validation
  • Add unit tests for parsePrUrlForRepoMatch, repo mismatch validation, checkPRStatus extended return, slingExistingPr validation, and forcePushAllowed defaults

Chunk 4 — BeadPanel babysit badge + metadata + event formatters:

  • Add "Babysat external PR" badge in BeadPanel header (sky-colored, Eye icon) when gt:babysit label is present
  • Add head_sha and force_push_allowed metadata cells in BeadPanel for babysat beads
  • Add human-readable event descriptions for babysit_started, pr_feedback_detected, pr_conflict_detected, pr_auto_merge, pr_status_changed in ActivityFeed
  • Add event icons and colors for the new event types in ActivityFeed
  • Update BeadInspectorDashboard with colored gt:babysit label badge, babysit metadata fields, and human-readable event labels
  • Export eventDescription from ActivityFeed for testability
  • Add unit tests for babysit event descriptions

Verification

  • Chunk 0: pnpm --filter cloudflare-gastown typecheck && pnpm --filter cloudflare-gastown test
  • Chunk 4: Manual — view a merge_request bead with gt:babysit label → badge renders, head_sha and force-push fields visible, activity timeline shows readable descriptions
  • Tests: services/gastown/test/unit/sling-existing-pr.test.ts and apps/web/src/components/gastown/babysit-ui.test.ts

Visual Changes

  • BeadPanel header: new sky-colored "Babysat external PR" badge with Eye icon for babysat beads
  • BeadPanel metadata grid: new "Head SHA" (monospace) and "Force-push" (Lock/Unlock icon) cells
  • Activity timeline: new icons and readable descriptions for babysit-related events
  • BeadInspectorDashboard: colored babysit label badge, head_sha/force-push fields, readable event labels

Reviewer Notes

  • Chunk 0 is the backend foundation; chunk 4 is the UI surface. Both target the same feature branch.
  • Source bead section in BeadPanel is already hidden for babysat beads (conditional on metadata.source_bead_id which babysit beads don't have).
  • The eventDescription function was exported from ActivityFeed to enable testing.
  • Component-level rendering tests for BeadPanel are not feasible in the current test infrastructure (no React testing library setup).

Comment thread services/gastown/src/dos/Town.do.ts Outdated
throw new Error(`Cannot babysit a PR that is already ${result.status}.`);
}

const headBranch = result.head_branch ?? '';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

WARNING: Empty-string fallback for head_branch/base_branch/head_sha silently produces a bead with blank fields

All three fields are optional in PRStatusResult (and in the underlying Zod schema), so they can legitimately be undefined if the GitHub/GitLab API omits them. Falling back to '' means submitExternalPrToReviewQueue is called with branch: '' and targetBranch: ''. The babysit loop (poll, conflict detection, auto-merge) will likely fail in confusing ways later rather than producing a clear error now.

Consider throwing early if any of these are missing instead:

const headBranch = result.head_branch;
const baseBranch = result.base_branch;
if (!headBranch || !baseBranch) {
  throw new Error(`PR metadata incomplete: head_branch=${headBranch}, base_branch=${baseBranch}`);
}

});
});

describe('forcePushAllowed default', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

WARNING: These tests exercise pure JS constant expressions, not production code

Both tests in 'forcePushAllowed default' test the expression undefined ?? false (a constant) and true (another constant) — they don't import or invoke any production code. They will always pass regardless of what happens in slingExistingPr. If someone changes the default in Town.do.ts, these tests will not catch it.

Consider replacing with an integration-style test that actually calls submitExternalPrToReviewQueue with the mock SQL and asserts on the stored force_push_allowed metadata value, or remove the describe block entirely if the production-code default is already covered implicitly.

import type { TownConfig } from '../../src/types';

class MiniSql {
private data: Map<string, unknown[]> = new Map();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SUGGESTION: MiniSql.data is an unused private field

The private data: Map<string, unknown[]> = new Map() field is never read or written by any method in the class. It should be removed to avoid dead code.

Suggested change
private data: Map<string, unknown[]> = new Map();
class MiniSql {

@@ -0,0 +1,286 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SUGGESTION: beforeEach and PRStatusResult are imported but never used

beforeEach is listed in the vitest import but never called in this file. PRStatusResult is imported from town-scm but never referenced. These will likely produce lint warnings and should be removed.

@kilo-code-bot

kilo-code-bot Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

All three previously flagged WARNINGs are resolved: babysit-ui.test.ts now imports from the new pure-TS event-description.ts module (no longer pulls in motion/react), and the forcePushAllowed tests now exercise real production code paths via submitExternalPrToReviewQueue.

Resolved Since Previous Review

The following issues from the prior review were fixed in commit 23544f2b0:

File Issue
apps/web/src/components/gastown/babysit-ui.test.ts ✅ Now imports from event-description.ts (pure TS, no 'use client' / motion/react transitive crash)
services/gastown/test/unit/sling-existing-pr.test.ts forcePushAllowed tests replaced with real integration-style tests exercising submitExternalPrToReviewQueue
apps/web/src/components/gastown/ActivityFeed.tsx eventDescription extracted to event-description.ts; file re-exports it for backward compatibility
Files Reviewed (4 files, incremental diff)
  • apps/web/src/components/gastown/event-description.ts — new pure-TS module, no issues
  • apps/web/src/components/gastown/ActivityFeed.tsx — re-export + icon/color additions, no issues
  • apps/web/src/components/gastown/babysit-ui.test.ts — import path corrected, no issues
  • services/gastown/test/unit/sling-existing-pr.test.ts — real tests added, positional indices verified correct against production INSERT order

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-4.6 · 1,216,625 tokens

Review guidance: REVIEW.md from base branch convoy/babysit-existing-pr-feature/60d71828/head

MessageCircle,
GitMergeIcon,
RefreshCw,
Zap,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SUGGESTION: Zap is imported but never used — dead import

None of the new EVENT_ICONS entries are mapped to Zap. This will trigger a lint error (no-unused-vars / @typescript-eslint/no-unused-vars).

Suggested change
Zap,
RefreshCw,

Head SHA
</dt>
<dd className="font-mono text-xs">
{(bead.metadata.head_sha as string).slice(0, 12)}…

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SUGGESTION: Redundant as string cast — TypeScript already knows this is string from the typeof … === 'string' guard on line 273

The enclosing condition typeof bead.metadata?.head_sha === 'string' narrows the type, so the cast can be removed per the project's as-avoidance rule.

Suggested change
{(bead.metadata.head_sha as string).slice(0, 12)}
{bead.metadata.head_sha.slice(0, 12)}

/>

{isBabysit && typeof bead.metadata?.head_sha === 'string' && (
<MetaCell icon={Hash} label="Head SHA" value={bead.metadata.head_sha as string} mono />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SUGGESTION: Redundant as string cast — the enclosing guard typeof bead.metadata?.head_sha === 'string' on line 477 already narrows the type

Per the project's coding standards, as casts should be avoided when flow-sensitive typing already provides the narrowing.

Suggested change
<MetaCell icon={Hash} label="Head SHA" value={bead.metadata.head_sha as string} mono />
<MetaCell icon={Hash} label="Head SHA" value={bead.metadata.head_sha} mono />

@jrf0110 jrf0110 changed the title feat(gastown): Town DO slingExistingPr method (babysit PR feature, chunk 0) feat(gastown+web): slingExistingPr + BeadPanel babysit badge (chunks 0+4) May 27, 2026
@@ -0,0 +1,94 @@
import { describe, it, expect } from '@jest/globals';
import { eventDescription } from '@/components/gastown/ActivityFeed';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

WARNING: Importing a runtime value from a 'use client' component in a Node Jest test will likely fail at module load time.

ActivityFeed.tsx has top-level imports of motion/react and useGastownTRPC (a tRPC React hook). When Jest loads this module to resolve eventDescription, it must evaluate the entire file. The Jest config uses testEnvironment: 'node' and motion is not listed in transformIgnorePatterns exceptions, so motion/react will be treated as opaque ESM — causing either a SyntaxError (ESM import in a CJS context) or a ReferenceError (browser globals like document/window) at test runtime.

Consider extracting the pure eventDescription function into its own module (e.g. event-description.ts) with no React/animation dependencies, then import from there in both ActivityFeed.tsx and the test.

John Fawcett added 2 commits May 27, 2026 17:50
…ePushAllowed tests

- Extract eventDescription from ActivityFeed.tsx to pure event-description.ts
  to avoid motion/react transitive import in Jest test
- Re-export from ActivityFeed.tsx for backwards compatibility
- Update babysit-ui.test.ts to import from event-description.ts
- Replace trivial forcePushAllowed inline tests with tests exercising
  submitExternalPrToReviewQueue through MiniSql mock
- Enhance MiniSql mock to capture INSERT statements and params
@jrf0110

jrf0110 commented May 27, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #3539 which targets the re-staged convoy branch.

@jrf0110 jrf0110 closed this May 27, 2026
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