Skip to content

Jobs: don't silently drop bare-org alwaysInclude specs (fix empty repo dropdown)#517

Merged
dhilgaertner merged 1 commit into
mainfrom
feature/crow-516-jobs-repo-dropdown
Jun 15, 2026
Merged

Jobs: don't silently drop bare-org alwaysInclude specs (fix empty repo dropdown)#517
dhilgaertner merged 1 commit into
mainfrom
feature/crow-516-jobs-repo-dropdown

Conversation

@dhilgaertner

Copy link
Copy Markdown
Contributor

Closes #516

Problem

In Settings → Jobs, creating a job for an org whose workspace config has a bare org name in alwaysInclude (e.g. SecurityScorecard's ["securityscorecard"]) showed "No repos found" with no explanation — you couldn't pick a repo, so you couldn't create the job.

The Jobs dropdown is populated by expanding alwaysInclude via ProviderManager.reposForSpecsclassifySpec. A bare token (no /, no *) classifies as .invalid and was silently skipped, so every entry got dropped and the single generic empty-state message couldn't explain why.

Fix

Rather than auto-coercing bare tokens to owner/* (which could mask a malformed owner/repo typo — those always contain a /), bare tokens stay .invalid but are now surfaced with an actionable hint.

  • WorkspaceRepoListing (CrowCore, new): resolved repos + invalidSpecs, each with an optional suggested fix.
  • reposForSpecs returns the listing, collecting invalid specs (with a "did you mean owner/*" suggestion for bare org names) instead of silently skipping. Whitespace-only entries are still dropped. classifySpec itself is unchanged.
  • JobFormView distinguishes three empty states: nothing configured / invalid spec(s) (with the suggestion) / valid specs that returned nothing (e.g. gh/glab not authenticated).
  • AppState, AppDelegate (incl. its repo cache), and SettingsView updated to the richer return type.

Tests

  • classifySpecBareNameIsInvalid kept (behavior unchanged) with a clarifying comment.
  • reposForSpecsKeepsExplicitSlugsSortedAndDeduped now also asserts the bare token is surfaced with a suggestion.
  • Added reposForSpecsSurfacesBareOrgTokenWithSuggestion (the CROW-516 repro), reposForSpecsSkipsWhitespaceOnlySpecs, and suggestionForInvalidSpecGlobsBareOrgName.

make app builds clean; CrowProvider/CrowCore/CrowUI/Crow test suites pass. (The only failing tests are pre-existing CrowGit RebaseTests that need a global git identity in the sandbox — unrelated to this change.)

Notes / follow-ups

🤖 Generated with Claude Code

The Jobs form's repo dropdown is populated by expanding a workspace's
alwaysInclude specs via ProviderManager.reposForSpecs. A bare org name
(e.g. "securityscorecard", no "/") classified as .invalid and was
silently dropped, leaving an empty dropdown with a generic "No repos
found" message — so SecurityScorecard's workspace couldn't create jobs.

Rather than auto-coercing bare tokens to owner/* (which could mask a
malformed owner/repo typo), keep them .invalid but surface them:

- New WorkspaceRepoListing (CrowCore): resolved repos + invalidSpecs,
  each with an optional suggested fix.
- reposForSpecs now returns the listing, collecting invalid specs
  (with a "did you mean owner/*" suggestion for bare org names) instead
  of silently skipping them. Whitespace-only specs are still dropped.
- JobFormView distinguishes three empty states: nothing configured /
  invalid spec(s) (with the suggestion) / valid specs that returned
  nothing (e.g. gh/glab not authenticated).
- AppState, AppDelegate (incl. cache), and SettingsView updated to the
  richer return type.
- Tests: bare org token is now surfaced (not dropped) with a suggestion;
  added reposForSpecs + suggestion(forInvalidSpec:) coverage.

🐦‍⬛ Generated with Claude Code, orchestrated by Crow

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 01959F97-2B60-4E9D-A00A-25399AFD2B06
@dhilgaertner dhilgaertner requested a review from dgershman as a code owner June 15, 2026 16:24

@dgershman dgershman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code & Security Review

Critical Issues

None.

Security Review

Strengths:

  • User-provided spec strings flow only into SwiftUI.Text (no markup interpretation) and into ProviderManager classification — they never reach shell() as concatenated arguments. Invalid specs are short-circuited before any provider CLI call.
  • The host / provider cache key (AppDelegate.swift:916-918) includes provider + host alongside alwaysInclude, so flipping provider/host within the TTL window won't return stale wrong-provider slugs. (Pre-existing — preserved here under the new listing type.)
  • New WorkspaceRepoListing is Sendable + Equatable; static let empty is safe for the actor-boundary closure default.

Concerns:

  • None.

Code Quality

  • Clean refactor: [String]WorkspaceRepoListing is threaded through every call site (AppState, AppDelegate, SettingsView, JobFormView) consistently, with .empty used as the closure default everywhere. No half-converted spots.
  • classifySpec is intentionally unchanged — bare tokens stay .invalid rather than being coerced to owner/*, which (as the PR notes) would mask owner/repo typos. Good call; the typo-vs-bare-org distinction matters.
  • The whitespace-only skip at ProviderManager.swift:252-253 prevents "'' isn't a valid spec" noise — and the suggestionForInvalidSpec tests pin that edge case down.
  • The loadGeneration stale-result guard at JobFormView.swift:177-186 is preserved and applies cleanly to both repos and invalidSpecs.
  • Test coverage on the new behavior is thorough: bare-org repro (securityscorecard), whitespace-only drop, mixed valid+invalid, suggestion edge cases (trailing *, /*, empty).
  • Tests pass locally (swift test --filter ProviderManagerTests → 52/52). CrowCore builds clean.

Consider (non-blocking)

  • The empty-state caption (JobFormView.swift:217) only renders when repoOptions.isEmpty. If a workspace's alwaysInclude is mixed (e.g. [\"securityscorecard\", \"myorg/myrepo\"]), the picker shows myrepo and the bare-org warning is silently swallowed — the user gets a working picker but no signal that one spec is malformed. The PR description scopes the fix to the empty case, so this is intentional, but a follow-up could surface invalid specs as a small caption alongside the populated picker for the mixed case.
  • invalidSpecs is an Array (not a Set), so a duplicate bare token in alwaysInclude would render twice. Vanishingly unlikely in practice and arguably honest about what the user wrote — not worth changing.

Summary Table

Color Meaning Verdict effect
Red Must fix Request changes
Yellow Should fix Request changes
Green Consider Approve allowed

Recommendation: Approve — driven by [0 Red, 0 Yellow, 2 Green] findings. Clean fix for CROW-516; explicit scope, thorough tests, type-safe refactor through the call chain.


🐦‍⬛ Reviewed by Crow via Claude Code

@dhilgaertner dhilgaertner merged commit 207583c into main Jun 15, 2026
2 checks passed
@dhilgaertner dhilgaertner deleted the feature/crow-516-jobs-repo-dropdown branch June 15, 2026 17:05
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.

Jobs settings: repo dropdown shows 'no repos found' — bare org name in alwaysInclude silently dropped

2 participants