@oss-autopilot/core - v3.42.2
    Preparing search index...

    Class StateManager

    Core module exports Re-exports all core functionality for convenient imports

    Index
    • Create a new StateManager instance.

      Parameters

      • inMemoryOnly: boolean = false

        When true, state is held only in memory and never read from or written to disk. Useful for unit tests that need isolated state without side effects. Defaults to false (normal persistent mode).

      Returns StateManager

    • get refreshGeneration(): number

      Number of times a Gist refresh replaced the in-memory state. A successful push resolves earlier un-pushed changes only if this did not advance in between (the #1443 loss-notice invariant); a long run compares it to the value at its start to learn its mutations were swapped out.

      Returns number

    • Add closed PRs to storage, deduplicating by URL. Entries with URLs that fail parseGitHubUrl are dropped before persistence (read-side filters in dashboard-data already skip them, but this prevents the bad data from reaching disk in the first place).

      Re-seen URLs upgrade the stored entry's missing ledger fields in place (#1461) — see addMergedPRs for the full semantics.

      Parameters

      • prs: KnownKeys<
            {
                backfilledAt?: string;
                closedAt: string;
                closeReason?: | "unknown"
                | "rejected"
                | string & {}
                | "ai-policy"
                | "superseded"
                | "stale"
                | "self-closed";
                commentsFetchedAt?: string;
                firstMaintainerResponseAt?: string;
                issueUrl?: string;
                lastCiStatus?: string;
                lastReviewDecision?: string;
                learningsExtractedAt?: string;
                openedAt?: string;
                pick?: {
                    capturedAt?: string;
                    commentCount?: number;
                    competingPRs?: number;
                    firstInRepo?: boolean;
                    grade: number;
                    issueAuthorAssociation?: | string & {}
                    | "OWNER"
                    | "MEMBER"
                    | "COLLABORATOR"
                    | "CONTRIBUTOR"
                    | "FIRST_TIMER"
                    | "FIRST_TIME_CONTRIBUTOR"
                    | "MANNEQUIN"
                    | "NONE";
                    issueAuthorIsBot?: boolean;
                    issueUrl: string;
                    labels?: string[];
                    mentioningPRs?: number;
                    recommendation: string;
                    source: string & {}
                    | "vet"
                    | "vet-list"
                    | "list";
                    stargazersCount?: number;
                    [key: string]: unknown;
                };
                responseRule?: number;
                selfCloseReason?: | string & {}
                | "superseded"
                | "no-traction"
                | "after-feedback"
                | "no-response";
                title: string;
                url: string;
                [key: string]: unknown;
            },
        >[]

        Closed PRs to add (duplicates by URL upgrade in place)

      Returns { added: number; dropped: number }

      count of entries added vs. dropped (invalid URL)

    • Track a new issue, or refresh the claim on one already tracked.

      Matching uses issueRefKey (same normalization as releaseIssue), so a re-claim through a differently cased or suffixed URL updates the existing entry instead of adding a duplicate. On a match only status and updatedAt are taken from issue: updatedAt is the claim stamp releaseIssue compares against the PR's close time (#1863), so a re-claim after the close must move it forward or the daily run would release the new claim as if it were the old one.

      Parameters

      • issue: {
            createdAt: string;
            id: number;
            labels: string[];
            number: number;
            repo: string;
            status: "candidate" | "claimed" | "in_progress" | "pr_submitted";
            title: string;
            updatedAt: string;
            url: string;
            vetted: boolean;
            vettingResult?: {
                checks: {
                    clearRequirements: boolean;
                    contributionGuidelinesFound: boolean;
                    noExistingPR: boolean;
                    notClaimed: boolean;
                    projectActive: boolean;
                };
                contributionGuidelines?: {
                    branchNamingConvention?: string;
                    claRequired?: boolean;
                    commitMessageFormat?: string;
                    formatter?: string;
                    issueClaimProcess?: string;
                    linter?: string;
                    prTitleFormat?: string;
                    rawContent?: string;
                    requiredChecks?: string[];
                    reviewProcess?: string;
                    styleGuideUrl?: string;
                    testCoverageRequired?: boolean;
                    testFileNaming?: string;
                    testFramework?: string;
                };
                notes: string[];
                passedAllChecks: boolean;
            };
        }

        The issue to track

      Returns void

    • Add merged PRs to storage, deduplicating by URL. Entries with URLs that fail parseGitHubUrl are dropped before persistence (read-side filters in dashboard-data already skip them, but this prevents the bad data from reaching disk in the first place).

      Re-seen URLs are not re-added, but upgrade the stored entry in place (#1461): outcome-ledger fields (openedAt, firstMaintainerResponseAt) missing on the stored entry are filled from the richer incoming one. Existing values are never overwritten, so stamps that live only on the stored entry (commentsFetchedAt, learningsExtractedAt) and earlier enriched writes stay intact.

      Parameters

      • prs: KnownKeys<
            {
                backfilledAt?: string;
                commentsFetchedAt?: string;
                firstMaintainerResponseAt?: string;
                issueUrl?: string;
                lastCiStatus?: string;
                lastReviewDecision?: string;
                learningsExtractedAt?: string;
                mergedAt: string;
                openedAt?: string;
                pick?: {
                    capturedAt?: string;
                    commentCount?: number;
                    competingPRs?: number;
                    firstInRepo?: boolean;
                    grade: number;
                    issueAuthorAssociation?: | string & {}
                    | "OWNER"
                    | "MEMBER"
                    | "COLLABORATOR"
                    | "CONTRIBUTOR"
                    | "FIRST_TIMER"
                    | "FIRST_TIME_CONTRIBUTOR"
                    | "MANNEQUIN"
                    | "NONE";
                    issueAuthorIsBot?: boolean;
                    issueUrl: string;
                    labels?: string[];
                    mentioningPRs?: number;
                    recommendation: string;
                    source: string & {}
                    | "vet"
                    | "vet-list"
                    | "list";
                    stargazersCount?: number;
                    [key: string]: unknown;
                };
                responseRule?: number;
                title: string;
                url: string;
                [key: string]: unknown;
            },
        >[]

        Merged PRs to add (duplicates by URL upgrade in place)

      Returns { added: number; dropped: number }

      count of entries added vs. dropped (invalid URL)

    • Add a repository to the trusted projects list. No-op if already trusted.

      Parameters

      • repo: string

        Repository in "owner/repo" format

      Returns void

    • Execute multiple mutations as a single batch, deferring disk I/O until the batch completes. Nested batch() calls are flattened — only the outermost saves.

      Parameters

      • fn: () => void

        The function containing mutations to batch

      Returns void

    • Push current state to Gist (async). Call at well-defined moments (end of daily, after claim).

      Returns Promise<boolean>

    • Remove excluded repos/orgs from trusted projects.

      Parameters

      • repos: string[]

        Repository names to exclude

      • orgs: string[]

        Organization names to exclude

      Returns void

    • Remove a manual status override for a PR.

      Parameters

      • url: string

        The PR URL

      Returns boolean

      true if an override was removed, false if none existed

    • Tombstone the guidelines file for repo so subsequent reads return null. Throws when not in Gist mode.

      Parameters

      • repo: string

      Returns void

    • Dismiss an issue's (or merged PR's, #1861) notifications. Auto-resurfaces on new activity.

      Parameters

      • url: string

        The issue or PR URL to dismiss

      • timestamp: string

        ISO timestamp of dismissal

      • options: { headSha?: string; refresh?: boolean } = {}
        • OptionalheadSha?: string

          For a reviewed PR (#1857): the head SHA the PR had when it was dismissed, recorded in config.dismissedPRHeads so the follow-up stays hidden until the head moves. Any head recorded earlier is replaced, or dropped when this dismiss names none.

        • Optionalrefresh?: boolean

          When set, an existing dismissal is re-stamped with timestamp instead of being left alone, so a second "skip" after newer activity moves the cutoff forward. Default keeps the original stamp.

      Returns boolean

      true if newly dismissed (or re-stamped under refresh), false if already dismissed

    • Mark the live claim on url finished at at (#1856): the branch is ready to collect, and expiresAt is extended so it stays collectable (see finishWorkClaim). Expired claims are pruned in the same write. Without a live claim nothing is written: a prune alone would mark the state un-pushed and raise a sync alarm for a change nobody made.

      Parameters

      • url: string
      • at: string
      • nowMs: number = ...

      Returns
          | KnownKeys<
              {
                  branch?: string;
                  expiresAt: string;
                  finishedAt?: string;
                  host?: string;
                  kind: string & {}
                  | "implementation"
                  | "pr";
                  machineId?: string;
                  runId?: string;
                  session: string;
                  startedAt: string;
                  status: "finished" | "running" | string & {};
                  url: string;
                  worktree?: string;
                  [key: string]: unknown;
              },
          >
          | undefined

      the updated claim, or undefined when no live claim exists

    • Gist mode: treat the current un-pushed saves as disposable, so the next refresh replaces them without raising the lost-changes staleness marker. Only for re-derivable data (the dashboard's GitHub fetch caches).

      Returns void

    • The claims still live at nowMs, in stored order.

      Parameters

      • nowMs: number = ...

      Returns KnownKeys<
          {
              branch?: string;
              expiresAt: string;
              finishedAt?: string;
              host?: string;
              kind: string & {}
              | "implementation"
              | "pr";
              machineId?: string;
              runId?: string;
              session: string;
              startedAt: string;
              status: "finished" | "running" | string & {};
              url: string;
              worktree?: string;
              [key: string]: unknown;
          },
      >[]

    • Returns all stored closed-without-merge PRs (sorted by close date descending via addClosedPRs).

      Returns KnownKeys<
          {
              backfilledAt?: string;
              closedAt: string;
              closeReason?: | "unknown"
              | "rejected"
              | string & {}
              | "ai-policy"
              | "superseded"
              | "stale"
              | "self-closed";
              commentsFetchedAt?: string;
              firstMaintainerResponseAt?: string;
              issueUrl?: string;
              lastCiStatus?: string;
              lastReviewDecision?: string;
              learningsExtractedAt?: string;
              openedAt?: string;
              pick?: {
                  capturedAt?: string;
                  commentCount?: number;
                  competingPRs?: number;
                  firstInRepo?: boolean;
                  grade: number;
                  issueAuthorAssociation?: | string & {}
                  | "OWNER"
                  | "MEMBER"
                  | "COLLABORATOR"
                  | "CONTRIBUTOR"
                  | "FIRST_TIMER"
                  | "FIRST_TIME_CONTRIBUTOR"
                  | "MANNEQUIN"
                  | "NONE";
                  issueAuthorIsBot?: boolean;
                  issueUrl: string;
                  labels?: string[];
                  mentioningPRs?: number;
                  recommendation: string;
                  source: string & {}
                  | "vet"
                  | "vet-list"
                  | "list";
                  stargazersCount?: number;
                  [key: string]: unknown;
              };
              responseRule?: number;
              selfCloseReason?: | string & {}
              | "superseded"
              | "no-traction"
              | "after-feedback"
              | "no-response";
              title: string;
              url: string;
              [key: string]: unknown;
          },
      >[]

    • Returns the most recent close date, used as a watermark for incremental fetching.

      Returns string | undefined

    • The head SHA recorded when a reviewed PR's follow-up was dismissed (#1857), or undefined when the PR is not dismissed or was dismissed without one.

      Parameters

      • url: string

        The PR URL to check

      Returns string | undefined

    • Single source of truth for gist persistence health (#1444). Surfaces that need "is this process reliably syncing to the Gist?" (daily warnings, dashboard banners/recovery gates) derive it from here instead of re-combining config.persistence / isGistMode() / isGistDegraded() at each call site.

      Composes the same primitive fields those accessors expose:

      • no gist store + config asks for gist → configured-but-local
      • gist store attached but disarmed (#1443) → bootstrap-degraded (since comes from the staleness marker the degraded bootstrap seeds)
      • otherwise healthy (degraded: null)

      Returns GistHealth

    • Read the per-repo guidelines for repo (#867). Returns null when in local mode, when no file exists, or when the file is empty (tombstoned).

      Parameters

      • repo: string

      Returns string | null

    • Returns repos above the score threshold.

      Parameters

      • OptionalminScore: number

        Minimum score (default: config.minRepoScoreThreshold)

      Returns string[]

    • Get the timestamp when an issue was dismissed, or undefined if not dismissed.

      Parameters

      • url: string

        The issue URL to check

      Returns string | undefined

    • Latest overnight run (#1574); undefined before the first one.

      Returns
          | KnownKeys<
              {
                  implementAttempts?: {
                      attemptedAt: string;
                      note?: string;
                      outcome: "blocked"
                      | "prepared";
                      url: string;
                      [key: string]: unknown;
                  }[];
                  implementUrl?: string;
                  judgmentCount: number;
                  prepareCount: number;
                  prepared: {
                      branch: string;
                      compareUrl?: string;
                      note?: string;
                      pushedAt?: string;
                      pushedRef?: string;
                      pushProblem?: string;
                      recordedAt: string;
                      url: string;
                      worktree?: string;
                      [key: string]: unknown;
                  }[];
                  reportPath: string;
                  runAt: string;
                  [key: string]: unknown;
              },
          >
          | undefined

    • Recovery details from the most recent state load, or null when the last load read the state file cleanly (or no load from disk has happened, e.g. in-memory mode). Set when loadState() fell back to a backup or fresh state because the main file was corrupt or invalid (#1371).

      Returns LoadRecoveryInfo | null

    • Returns repos below the score threshold.

      Parameters

      • OptionalmaxScore: number

        Maximum score (default: config.minRepoScoreThreshold)

      Returns string[]

    • Returns all stored merged PRs (sorted by merge date descending via addMergedPRs).

      Returns KnownKeys<
          {
              backfilledAt?: string;
              commentsFetchedAt?: string;
              firstMaintainerResponseAt?: string;
              issueUrl?: string;
              lastCiStatus?: string;
              lastReviewDecision?: string;
              learningsExtractedAt?: string;
              mergedAt: string;
              openedAt?: string;
              pick?: {
                  capturedAt?: string;
                  commentCount?: number;
                  competingPRs?: number;
                  firstInRepo?: boolean;
                  grade: number;
                  issueAuthorAssociation?: | string & {}
                  | "OWNER"
                  | "MEMBER"
                  | "COLLABORATOR"
                  | "CONTRIBUTOR"
                  | "FIRST_TIMER"
                  | "FIRST_TIME_CONTRIBUTOR"
                  | "MANNEQUIN"
                  | "NONE";
                  issueAuthorIsBot?: boolean;
                  issueUrl: string;
                  labels?: string[];
                  mentioningPRs?: number;
                  recommendation: string;
                  source: string & {}
                  | "vet"
                  | "vet-list"
                  | "list";
                  stargazersCount?: number;
                  [key: string]: unknown;
              };
              responseRule?: number;
              title: string;
              url: string;
              [key: string]: unknown;
          },
      >[]

    • Returns the most recent merge date, used as a watermark for incremental fetching.

      Returns string | undefined

    • The rendered morning report as last published to the Gist (#1698), so a machine other than the one that ran overnight can read it. Null in local mode or before the first publish.

      Returns string | null

    • Vet-time predictions awaiting a ledger join (#1824), oldest first.

      Returns KnownKeys<
          {
              capturedAt?: string;
              commentCount?: number;
              competingPRs?: number;
              firstInRepo?: boolean;
              grade: number;
              issueAuthorAssociation?: | string & {}
              | "OWNER"
              | "MEMBER"
              | "COLLABORATOR"
              | "CONTRIBUTOR"
              | "FIRST_TIMER"
              | "FIRST_TIME_CONTRIBUTOR"
              | "MANNEQUIN"
              | "NONE";
              issueAuthorIsBot?: boolean;
              issueUrl: string;
              labels?: string[];
              mentioningPRs?: number;
              recommendation: string;
              source: string & {}
              | "vet"
              | "vet-list"
              | "list";
              stargazersCount?: number;
              [key: string]: unknown;
          },
      >[]

    • Get the score record for a repository.

      Parameters

      • repo: string

        Repository in "owner/repo" format

      Returns
          | Readonly<
              KnownKeys<
                  {
                      avgResponseDays: number
                      | null;
                      closedWithoutMergeCount: number;
                      language?: string | null;
                      lastEvaluatedAt: string;
                      lastMergedAt?: string;
                      mergedPRCount: number;
                      repo: string;
                      score: number;
                      signals: {
                          hasActiveMaintainers: boolean;
                          hasHostileComments: boolean;
                          isResponsive: boolean;
                      };
                      stargazersCount?: number;
                      [key: string]: unknown;
                  },
              >,
          >
          | undefined

      Read-only score record, or undefined if not tracked

    • Returns repository names that have at least one merged PR.

      Returns string[]

    • Returns repository names with open PRs but no merged PRs yet.

      Returns string[]

    • Broad-phase language rotation cursor (#1630); undefined before the first search.

      Returns
          | KnownKeys<
              {
                  languageOffset: number;
                  lastRotatedAt?: string;
                  maintainedOffset: number;
                  phase0Offset: number;
                  starredOffset: number;
                  strategyOffset: number;
                  [key: string]: unknown;
              },
          >
          | undefined

    • The durable search seen-set (#1528) — issue URLs surfaced by recent searches, fed into oss-scout for result rotation. Returns [] when none recorded yet.

      Returns { lastSeenAt: string; url: string }[]

    • Returns cached starred repository names.

      Returns string[]

    • Returns a staleness marker when the in-memory state diverged from the canonical Gist (refresh failure or degraded bootstrap), or null when state is current. Commands surface this via their --json warnings envelope (#1193).

      Returns StalenessInfo | null

    • Returns aggregate contribution statistics (merge rate, PR counts, repo breakdown).

      Returns Stats

    • Get the status override for a PR, auto-clearing if new activity has occurred.

      Parameters

      • url: string

        The PR URL

      • OptionalcurrentUpdatedAt: string

        PR's current updatedAt timestamp for staleness check

      Returns
          | {
              lastActivityAt: string;
              setAt: string;
              status: "needs_addressing"
              | "waiting_on_maintainer";
          }
          | undefined

      The override if still valid, undefined otherwise

    • Every stored claim, expired ones included; getActiveWorkClaims is the usual read.

      Returns KnownKeys<
          {
              branch?: string;
              expiresAt: string;
              finishedAt?: string;
              host?: string;
              kind: string & {}
              | "implementation"
              | "pr";
              machineId?: string;
              runId?: string;
              session: string;
              startedAt: string;
              status: "finished" | "running" | string & {};
              url: string;
              worktree?: string;
              [key: string]: unknown;
          },
      >[]

    • Gist mode: whether a saved mutation has not reached the Gist yet.

      Returns boolean

    • Initialize state with sensible defaults for zero-config onboarding. No-op if setup is already complete.

      Parameters

      • username: string

      Returns void

    • Whether the Gist is in degraded mode (using local cache fallback).

      Returns boolean

    • Whether this StateManager is backed by a Gist.

      Returns boolean

    • Whether per-repo guidelines (#867) are available. True iff the Gist store is initialized — in local-only mode, guidelines are unavailable and write operations would throw GuidelinesNotAvailableError.

      Returns boolean

    • Check if a PR is currently shelved.

      Parameters

      • url: string

        The PR URL to check

      Returns boolean

      true if the PR is shelved

    • Check if initial setup has been completed.

      Returns boolean

    • Returns true if starred repos cache is older than 24 hours.

      Returns boolean

    • List repos with non-empty guidelines stored in the Gist.

      Returns string[]

    • Stamp commentsFetchedAt on the merged or closed PR matching url (#867). No-op when no PR with that URL is stored.

      Parameters

      • url: string
      • fetchedAt: string

      Returns void

    • Stamp learningsExtractedAt on the merged or closed PR matching url (#867). No-op when no PR with that URL is stored.

      Parameters

      • url: string
      • extractedAt: string

      Returns void

    • Mark setup as complete and record the completion timestamp.

      Returns void

    • Record vet-time predictions (#1824). The first prediction for an issue wins: a later re-vet (often after the user's own PR exists) must not replace the one that picked it. The oldest entries are dropped past MAX_PICK_SNAPSHOTS.

      ponytail: never refreshed, so an issue vetted months before it is picked keeps its old grade. Replace snapshots older than N days if that skews calibration.

      Parameters

      • snapshots: KnownKeys<
            {
                capturedAt?: string;
                commentCount?: number;
                competingPRs?: number;
                firstInRepo?: boolean;
                grade: number;
                issueAuthorAssociation?: | string & {}
                | "OWNER"
                | "MEMBER"
                | "COLLABORATOR"
                | "CONTRIBUTOR"
                | "FIRST_TIMER"
                | "FIRST_TIME_CONTRIBUTOR"
                | "MANNEQUIN"
                | "NONE";
                issueAuthorIsBot?: boolean;
                issueUrl: string;
                labels?: string[];
                mentioningPRs?: number;
                recommendation: string;
                source: string & {}
                | "vet"
                | "vet-list"
                | "list";
                stargazersCount?: number;
                [key: string]: unknown;
            },
        >[]

      Returns void

    • Re-fetch state from the backing Gist (if in Gist mode). Throttled to once per 30 seconds by GistStateStore unless force is set. Returns true if state was refreshed.

      Parameters

      • options: { force?: boolean } = {}

      Returns Promise<boolean>

    • Release a claim: drop the issue from activeIssues (#1863). Called by the daily run when the user closed their own PR for the issue unmerged.

      What changes: the issue leaves the tracked set that IssueConversationMonitor.fetchCommentedIssues excludes, so from the next run it re-enters the commented-issue monitor like any other issue the user has commented on, and it stops reading as in progress wherever activeIssues is listed. Capacity is NOT affected: assessCapacity counts open PRs only, never tracked issues.

      Matching uses issueRefKey (case-insensitive owner/repo, trailing slash and fragment ignored), falling back to the exact string for a URL that does not parse. The removal cannot be undone, so with claimedBefore set the entry is kept when its updatedAt is at or after that instant: updatedAt is the claim stamp the claim command writes (createdAt there is the issue's GitHub creation date), so a claim made after the PR closed is a deliberate re-claim that must survive. Unparseable timestamps keep the entry for the same reason.

      Parameters

      • issueUrl: string

        URL of the issue to release

      • OptionalclaimedBefore: string

        ISO instant (the PR's close time); only a claim recorded strictly before it is released

      Returns ReleaseIssueResult

    • Drop claims: the one on url, or every claim held by session (optionally only of one kind). Expired claims are pruned in the same write; when nothing matches, nothing is written (see finishWorkClaim).

      Parameters

      • selector: { kind?: string; session?: string; url?: string }
      • nowMs: number = ...

      Returns string[]

      the URLs of the live claims released

    • Re-read state from disk if the file has been modified since the last load/save. Returns true if state was reloaded, false if unchanged or in-memory mode.

      Returns boolean

    • Move a transferred repo's stored PRs and score to its new name. See repoScoring.renameRepo.

      Parameters

      • from: string
      • to: string

      Returns void

    • Run fn as an in-flight mutation: non-forced refreshFromGist() calls are skipped until it settles so a concurrent refresh cannot swap this.state out from under it (audit 2026-10-30 M2).

      Deliberately a counter, not a serializing promise chain: parallel MCP tool bodies from one host message may depend on each other through the host, so queuing them would deadlock. The gap that leaves (a sibling's forced conflict-recovery refresh swaps the state object mid-body) is closed by maybeCheckpoint re-applying every mutation right before its push; bodies that cannot re-apply (daily) watch refreshGeneration.

      Type Parameters

      • T

      Parameters

      • fn: () => Promise<T>

      Returns Promise<T>

    • Persist the current state to disk, creating a timestamped backup of the previous state file first. In in-memory mode, only updates lastRunAt without any file I/O.

      In Gist mode, writes to a local cache file (not the main state file) so the Gist remains the source of truth. Use checkpoint() to push state to the Gist.

      Local-file mode uses optimistic compare-and-swap: if another process has written state.json since we last loaded/saved, saveState throws ConcurrencyError. See issue #1030.

      Parameters

      • options: { allowReloadAndLoseMutation?: boolean } = {}

      Returns void

      ConcurrencyError (local file mode only) when the on-disk state was modified externally between this StateManager's last load/save and now. Callers that tolerate silent merge can set { allowReloadAndLoseMutation: true } to downgrade the error into a warning — but note that doing so abandons any in-memory mutation made since the last load. Fail-loud (the default) is preferred.

    • Persist per-repo guidelines for repo. Throws when not in Gist mode or when content exceeds the byte budget.

      Parameters

      • repo: string
      • content: string

      Returns void

    • Store the latest daily digest and update the digest timestamp.

      Parameters

      • digest: {
            autoUnshelvedPRs: {
                daysSinceActivity: number;
                number: number;
                repo: string;
                status: "needs_addressing" | "waiting_on_maintainer";
                title: string;
                url: string;
            }[];
            generatedAt: string;
            needsAddressingPRs: FetchedPR[];
            openPRs: FetchedPR[];
            recentlyClosedPRs: {
                closedAt: string;
                closedBy?: string;
                issueUrl?: string;
                number: number;
                openedAt?: string;
                repo: string;
                title: string;
                url: string;
            }[];
            recentlyMergedPRs: {
                issueUrl?: string;
                mergedAt: string;
                number: number;
                openedAt?: string;
                repo: string;
                title: string;
                url: string;
            }[];
            shelvedPRs: {
                daysSinceActivity: number;
                number: number;
                repo: string;
                status: "needs_addressing"
                | "waiting_on_maintainer";
                title: string;
                url: string;
            }[];
            summary: {
                mergeRate: number;
                totalActivePRs: number;
                totalMergedAllTime: number;
                totalNeedingAttention: number;
            };
            waitingOnMaintainerPRs: FetchedPR[];
        }

        The daily digest to store

      Returns void

    • Persist the timestamp of the most recent strategy snapshot embedded in a daily run output (#1270). Called from the daily pipeline after computeStrategy() succeeds; the cadence gate in shouldComputeStrategy reads this on the next run.

      Parameters

      • iso: string

      Returns void

    • Overwrite the first-response lookup on the merged or closed PR matching url. Unlike addMergedPRs/addClosedPRs, which only fill missing fields, this replaces firstMaintainerResponseAt (clearing it when at is undefined) so ledger-backfill can recompute values recorded under an older maintainer rule. No-op when no PR with that URL is stored.

      Parameters

      • url: string
      • at: string | undefined
      • rule: number

      Returns void

    • Update the local repository cache.

      Parameters

      • cache: {
            cachedAt: string;
            repos: Record<
                string,
                { currentBranch: string
                | null; exists: boolean; path: string },
            >;
            scanPaths: string[];
        }

        Local repository cache mapping repo names to paths

      Returns void

    • Update monthly closed PR counts for dashboard display.

      Parameters

      • counts: Record<string, number>

        Monthly closed PR counts keyed by YYYY-MM

      Returns void

    • Update monthly merged PR counts for dashboard display.

      Parameters

      • counts: Record<string, number>

        Monthly merged PR counts keyed by YYYY-MM

      Returns void

    • Update monthly opened PR counts for dashboard display.

      Parameters

      • counts: Record<string, number>

        Monthly opened PR counts keyed by YYYY-MM

      Returns void

    • Stage the rendered morning report for the next Gist push (#1698). Freeform document, last-write-wins like guidelines. No-op in local mode: the file on disk is the only copy there, which is fine on one machine.

      Parameters

      • content: string

      Returns void

    • Replace the search seen-set (#1528). Callers merge the just-surfaced URLs and cull stale entries before calling this; the manager only persists.

      Parameters

      • seen: { lastSeenAt: string; url: string }[]

      Returns void

    • Update the cached starred repositories and timestamp.

      Parameters

      • repos: string[]

        Repository names in "owner/repo" format

      Returns void

    • Parameters

      • url: string
      • status: "needs_addressing" | "waiting_on_maintainer"
      • lastActivityAt: string

      Returns void

    • Shelve a PR URL, hiding it from daily digest and capacity.

      Parameters

      • url: string

        The PR URL to shelve

      Returns boolean

      true if newly shelved, false if already shelved

    • Attempt PR count reconciliation, logging a warning on failure. Called after every state load and ledger addition, and by daily after it overwrites counts from the pagination-capped search (#1897).

      Returns void

    • Restore a dismissed issue to notifications. A recorded dismiss head (#1857) goes with it.

      Parameters

      • url: string

        The issue URL to undismiss

      Returns boolean

      true if undismissed, false if not currently dismissed

    • Unshelve a PR URL, restoring it to daily digest.

      Parameters

      • url: string

        The PR URL to unshelve

      Returns boolean

      true if removed from shelf, false if not shelved

    • Merge partial config updates into the current configuration.

      Parameters

      • config: Partial<AgentState["config"]>

        Partial config object to merge

      Returns void

    • Update scoring data for a repository.

      Parameters

      • repo: string

        Repository in "owner/repo" format

      • updates: RepoScoreUpdate

        Partial score fields to merge

      Returns void

    • Record a claim, replacing any claim on the same item (#1856). Expired claims are pruned in the same write. An absolute set: re-running it on a refreshed Gist baseline keeps every other machine's claims.

      Parameters

      Returns boolean

      true when a live claim on the item was replaced

    • Async factory that creates a StateManager backed by a GitHub Gist.

      The regular constructor is synchronous (for backwards-compat), but Gist bootstrapping requires network calls, so this factory is async.

      Parameters

      • token: string

        GitHub personal access token with gist scope

      Returns Promise<StateManager>