ResearchOperator Surface

GitHub Link Tracking & Live Status: Design Exploration

Status: Design exploration for GitHub link tracking & live status (Phase 2, Agent Orchestration Program) — not yet implemented

Problem

Once the agent has emitted <jackin:issue> or <jackin:pr> tags via the agent tag protocol, the operator wants to see their live state: is the issue still open? Did the PR get approved? Did CI go green? Today jackin has no GitHub status awareness at all — the operator switches to a browser tab to check.

Polling GitHub for every link on every console redraw would burn the rate limit instantly, so this needs a small cache and an awareness of which states change rarely vs frequently.

Why It Matters

  • It's the most operator-visible payoff of the tag protocol — without live status, the protocol just stores URLs.
  • It lets the autonomous queue (Phase 4) act on PR state ("re-queue a task whose PR was rejected") without re-implementing GitHub polling inside the queue.
  • It's the lowest-risk way to introduce a third-party network dependency (Octocrab) into jackin runtime — the surface is narrow (read-only status fetches) and the failure mode is obvious (status cell shows "?").

Inspiration in multicode

Sources:

multicode polls GitHub via Octocrab on a per-link cadence that varies by state. Cached in per-workspace SQLite (.multicode/cache.sqlite), table github_link_statuses. Refresh windows:

StateRefresh after
Open issue10 min
Closed issue60 min
PR building1 min
PR built, review pending5 min
PR approved, awaiting merge10 min
PR closed/merged60 min
On fetch error5 min retry

Cached fields per row include: URL, kind (issue/PR), host, owner, repo, resource number, issue state, PR state, build state, review state, draft flag, fetched-at, refresh-after, last error.

The TUI renders icons:

  • Issue: blue circle (open), gray X (closed)
  • PR: state colors plus draft badge, build status (running/success/fail), review status (none/pending/changes/approved)

Reuse multicode's polling cadence model verbatim — they did the work to calibrate it against real GitHub usage. Use Octocrab as the client. Persist via the persistent storage layer.

Auth source

Reuses the credential source pattern: the operator's GitHub PAT comes from one of env, command, op://, or keychain. Required scopes: public_repo for public repos, repo for private. read:user for resolving avatars later.

The existing gh CLI passthrough is separate and remains how the container talks to GitHub. The link-tracking client is operator-host only — the agent doesn't see the token.

Schema (in the SQLite layer)

CREATE TABLE github_link_statuses (
    url TEXT PRIMARY KEY,
    kind TEXT NOT NULL,                  -- 'issue' or 'pr'
    host TEXT NOT NULL,
    owner TEXT NOT NULL,
    repo TEXT NOT NULL,
    resource_number INTEGER NOT NULL,
    issue_state TEXT,                    -- 'open' | 'closed' | NULL
    pr_state TEXT,                       -- 'open' | 'merged' | 'closed' | NULL
    build_state TEXT,                    -- 'pending' | 'success' | 'failure' | 'error' | NULL
    review_state TEXT,                   -- 'none' | 'pending' | 'changes_requested' | 'approved'
    pr_is_draft BOOLEAN,
    fetched_at INTEGER NOT NULL,         -- epoch seconds
    refresh_after INTEGER NOT NULL,      -- epoch seconds
    last_error TEXT
);

Cache is per-instance (each ~/.jackin/data/<container>/ has its own SQLite). Sharing a cache across instances would be a small efficiency win but creates state-isolation questions — defer.

Fetcher loop

A single async task per running console session (not per-instance) walks all known links across instances, picks ones whose refresh_after is in the past, fetches in batches of N (default 5) to avoid burst rate limit consumption, updates the cache.

Failures: log to last_error, set refresh_after = now + 5 min, console renders a yellow "?" until success.

Console rendering

Each instance row gets a "Links" cell (or expand-to-detail panel) that shows captured <jackin:issue> and <jackin:pr> markers as colored icons, with hover/select revealing details. Clicking opens via the web handler.

Scope (V1)

  • Octocrab-based fetcher with multicode's cadence table.
  • Per-instance SQLite cache (depends on persistent storage layer).
  • Console icons for issue/PR state, build, review.
  • Operator-host PAT via the existing credential resolution (op://, ${env.VAR}, literal). New backends added when credential source pattern lands.
  • Console "Links" panel/cell rendering.
  • Click-to-open via the web handler from operator handler system.

Defer

  • Custom-link CRUD from the console (multicode allows manually adding links not emitted by the agent). Useful but waits — the agent-emitted flow covers the 95% case.
  • GitHub Enterprise host support beyond the standard https://github.com/<owner>/<repo> URL pattern. multicode supports it; jackin V1 should too if cost is low (Octocrab handles GHE clients with one extra config line). Confirm in design.
  • PR review summary text (who approved, what comments). Just state in V1.
  • Cross-link aggregation ("show me all open PRs across all instances"). Defer until Phase 4.
  • GitLab / Bitbucket. Defer indefinitely; if demand surfaces, abstract the fetcher behind a LinkProvider trait.

Open Questions

  • Per-instance cache vs operator-global. Per-instance matches jackin existing data layout but means a link tracked by two instances gets fetched twice. Recommended default: per-instance for V1; revisit if rate-limit pressure shows up.
  • Token scope guidance. What's the minimum-privilege token the docs should recommend? public_repo works for public-only; private repos need repo. multicode docs are clear on this; jackin docs should match.
  • GitHub App alternative. Long-term, a GitHub App (with per-installation tokens) is more durable than a PAT. Defer the design but flag it for the multi-tenant Kubernetes future.
  • New module (e.g. src/runtime/link_tracking.rs) — fetcher loop + cadence + cache writes
  • crates/jackin-capsule/src/pr_context.rs — existing focused-instance PR context this feature extends
  • src/console/tui/view/ — icon rendering
  • The persistent-storage module owns the SQL schema

See Also

On this page