Skip to content

GitHub lifecycle recovery incorrectly depends on the intake label #205

Description

@willwashburn

Problem

GitHub-native Factory overloads safety.requireLabel (for example, factory-ready) as both:

  1. the intake signal for new work, and
  2. a precondition for operating on work already in factory:in-progress.

That makes a normal human action — removing factory-ready to mean “do not pick this up as new work” — silently strand existing state. The live repro is AgentWorkforce/hoopsheet#32, which has remained factory:in-progress without factory-ready for 11 days.

Evidence and the initial two-site finding come from head-of-experiments’ hoopsheet/factory survey. They also tested the obvious ownership-scoping objection: requireLabel is not what scopes a Factory instance to repositories. Repo routing is handled separately through repos.names / repos.byLabel (#repoLabelForGithubIssue and its githubIssueMirrorsSkippedUnroutable path). Separating intake from lifecycle state does not weaken repository ownership.

Code sweep

An exhaustive rg sweep of requireLabel across src/ found the following GitHub lifecycle sites in src/orchestrator/factory.ts that incorrectly depend on the intake label after work is already in progress:

  • runOnce() builds mayRecoverGithubOrphan only when both the required intake label and factory:in-progress are present (around 1738).
  • #reconcileOrphanedGithubInProgress() repeats the same precondition (around 1907).
  • #isGithubIssueResumable() repeats it again (around 4050), so the issue is unresumable as well as unreconcilable.
  • #clarificationIssueStillActive() treats an in-progress issue as inactive when the intake label was removed (around 12668), cancelling durable clarification wake.
  • #githubIssuePathsFromIndex() only enumerates open issues carrying the intake label (around 4519). On the current indexed fast path, an orphan like Live factory start should always run startup backfill after subscribing #32 can disappear before the reconciler sees it at all.

Other requireLabel uses are legitimate intake/scope checks (#isIssueReady, live change ingestion, dispatch safety, routing-label exclusion) and should remain intake-driven.

Expected behavior

  • New-work discovery and dispatch should continue to require safety.requireLabel.
  • Lifecycle recovery, resume, clarification wake, and discovery of already-owned lifecycle state should key on factory:in-progress (plus terminal/closed and active-owner guards), not on the intake label.
  • The indexed discovery path must include nonterminal Factory lifecycle labels needed for reconciliation, even when the intake label is absent.
  • Removing the intake label must not erase or disable cleanup of existing Factory state.

Fix direction

Introduce distinct predicates for:

  • intake eligible: open + configured required label + not terminal/in-progress as appropriate;
  • Factory lifecycle active/recoverable: open + factory:in-progress + not factory:human-review, with the existing durable-owner/roster safety checks.

Use the lifecycle predicate consistently in orphan discovery, reconciliation, resume, and clarification wake. Add regression coverage for an open factory:in-progress issue without factory-ready, including the _index.json fast path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions