GitHub Sync is a Paperclip plugin for registering one or more GitHub repositories and synchronizing their open issues into Paperclip projects.
The plugin MUST provide a settings page inside Paperclip where an operator can configure:
- a GitHub token stored as a company-scoped Paperclip secret reference
- an optional external config file at
${PAPERCLIP_HOME:-~/.paperclip}/plugins/github-sync/config.jsonfor worker-only global values such as a rawgithubToken - Paperclip board access, which is optional but configurable on
deploymentMode: "local_trusted"deployments and required when the Paperclip deployment reportsdeploymentMode: "authenticated" - one or more GitHub repository mappings
- company-scoped advanced defaults for imported issues: default assignee, default Paperclip status, executor/reviewer/approver handoff assignees for sync-driven status transitions, and ignored GitHub issue authors, where a saved username such as
renovatealso matches GitHub bot logins such asrenovate[bot] - the frequency for automatic scheduled sync runs for that company
- a Paperclip project name per mapping where synchronized issues should be created, including existing Paperclip projects that are already bound to a GitHub repository workspace
The settings page MUST allow saving mappings and triggering a manual sync.
- When the settings page is opened with a Paperclip company context, it MUST only display and save repository mappings for that company, and saving one company’s mappings MUST preserve mappings that belong to other companies.
- When the settings page is opened with a Paperclip company context, it MUST only display and save advanced issue defaults for that company, and saving one company’s defaults MUST preserve defaults that belong to other companies.
- The settings page SHOULD clearly label company-scoped setup and defaults, including sync cadence, for the active company.
- When a company context is present, the settings page SHOULD show the active company name prominently using a human-friendly label instead of a raw identifier.
- When a company already has Paperclip projects bound to GitHub repository workspaces, the settings page SHOULD surface those projects so an operator can enable sync without recreating the project.
- The settings page MUST render the Paperclip board-access connect controls when the current Paperclip deployment reports
deploymentMode: "authenticated"ordeploymentMode: "local_trusted", while only treating board access as required for sync when the deployment reportsdeploymentMode: "authenticated". - When the settings page successfully validates a saved GitHub token, it SHOULD persist the validated GitHub login as non-secret display metadata so later visits can continue showing
Authenticated as ...instead of falling back to a generic ready state. - When the settings page successfully connects Paperclip board access for a company, it SHOULD persist a company-scoped non-secret identity label so later visits can continue showing
Connected as ...instead of falling back to a generic connected state. - When the active company has a saved Paperclip board-access user id, every advanced-settings assignee dropdown MUST include a
Meoption that maps to that connected board user alongside the available agents. - Execution-policy fallback dropdown copy SHOULD use operator-facing language such as
Automatic routingand explain that Paperclip execution-policy participants andreturnAssigneewin before company-level reviewer, approver, or executor fallbacks. - The plugin SHOULD also expose manual sync entry points from Paperclip toolbar surfaces when the SDK supports them.
- When a manual sync will outlive a quick action response, the worker MUST persist a
runningsync state immediately and complete the sync asynchronously. - Once a sync is running, the settings page, dashboard widget, and toolbar sync entry points MUST provide a way to request cancellation, and the worker MUST stop cooperatively after the current repository or issue step finishes.
- If a worker restart or similar interruption leaves a persisted sync state in
runningwithout a live in-memory sync behind it, the next worker-visible read or control path MUST reconcile that orphaned run into an interrupted error or cancelled result instead of leaving the UI stuck inrunningor silently restarting the abandoned run. - The settings page, dashboard widget, and sync toolbar surfaces SHOULD detect authenticated deployments from
/api/healthand MUST require connected Paperclip board access before enabling sync for the affected company.
- The raw GitHub token MUST NOT be persisted in plugin state.
- Saving a token from the settings UI MUST create or reuse a company secret through the Paperclip host API.
- The plugin MUST persist only the resulting secret UUID, keyed by company, in plugin state and MUST mirror it into that company's plugin config as the shared
{ "type": "secret_ref", "secretId": "<uuid>" }binding, because Paperclip2026.831binds and resolves plugin secret refs only from that shape and only for the company that owns the config row. - The worker MUST resolve that secret at runtime via
ctx.secrets.resolve({ type: "secret_ref", secretId }, { companyId, configPath }), MUST accept both the binding object and the pre-2026.831bare UUID string when reading plugin config or plugin state, and MUST report a config-repair signal to the settings UI when plugin config still holds a bare UUID so the UI re-mirrors it as a binding. - UI-side plugin config writes MUST recover from the known plugin-secret-reference-disabled failure by retrying without plugin secret-ref maps, so saving non-secret settings cannot be blocked by stale secret-ref config.
- If a Paperclip host cannot resolve a plugin secret ref for the worker (the pre-
2026.831plugin-secret-reference-disabled failure, a2026.831binding_missing/invalid-reference failure for a ref that is not bound as a binding, or a denied company scope), the settings UI MAY ask the worker to persist the validated raw token in a worker-local company-scoped fallback map at${PAPERCLIP_HOME:-~/.paperclip}/plugins/github-sync/config.jsonso production sync can continue without writing the raw token to plugin state or plugin config. Host-resolved secret refs remain the primary path. - The plugin MAY persist lightweight non-secret display metadata such as the validated GitHub login alongside the saved GitHub token secret ref so hosted UI can keep connected-state copy consistent across refreshes without resolving the secret.
- The settings UI MUST NOT propagate the saved GitHub token into agent
adapterConfig.env; authenticated agents should use Paperclip's plugin tool dispatcher for GitHub Sync tools instead of receivingGITHUB_TOKENfrom this plugin. - The settings UI MAY offer an explicitly opt-in, default-off control that additionally stores the same validated GitHub token as a company secret named exactly
GITHUB_TOKEN, which is the name Paperclip's own GitHub features probe for managed-checkout git credentials, merged-PR confirmation, workspace reaping, and external-object liveness. When enabled it MUST match the existing secret name exactly because the host compares secret names byte for byte, MUST create that secret or rotate the existing one in place rather than creating a second copy, MUST refuse to rotate a secret the host reports as non-active, MUST NOT change the plugin's own secret reference, and a failure to write it MUST NOT fail the GitHub token save. - If
${PAPERCLIP_HOME:-~/.paperclip}/plugins/github-sync/config.jsonexists and contains either a stringgithubTokenor agithubTokensByCompanyIdmap, the worker MUST treat those values as worker-only fallback sources for the GitHub token without persisting or returning those raw tokens. - The raw Paperclip board API token MUST NOT be persisted in plugin state.
- Connecting Paperclip board access from the settings UI MUST create or reuse a company secret through the Paperclip host API and MUST persist only the resulting secret UUID, keyed by company in plugin state and mirrored into plugin instance config when the host allows plugin secret refs there.
- If the host cannot resolve saved board-access secret refs for plugin workers, the settings UI MAY pass the approved raw board token to a worker action that persists a company-scoped worker-local fallback in
${PAPERCLIP_HOME:-~/.paperclip}/plugins/github-sync/config.jsonso direct Paperclip REST sync calls can continue without storing the raw board token in plugin state or plugin config. - The plugin MAY persist lightweight company-scoped non-secret display metadata such as the connected board identity label alongside the saved board token secret ref so hosted UI can keep connected-state copy consistent across refreshes without resolving the secret.
- The worker MUST resolve the saved Paperclip board token secret at runtime via
ctx.secrets.resolve(...)before making direct Paperclip REST calls for that company, falling back to the company-scoped worker-local board token only when host secret-ref resolution is unavailable.
The plugin MUST persist repository mappings, company-scoped advanced issue defaults, and sync state in plugin state.
- The worker MUST expose at least one data endpoint for reading the current settings and sync status.
- The worker MUST expose action endpoints for saving mappings and triggering a manual sync.
- The worker MUST expose a company-scoped dashboard KPI read model that returns backlog history, GitHub issue-closure activity, and Paperclip-attributed pull request activity for the dashboard widget.
- The worker SHOULD expose project-scoped pull request read models and actions for mapped repositories, including a lightweight queue read, a lightweight open-count read for sidebar badges, an on-demand detail read, and row-level actions for linking a Paperclip issue, updating a behind-but-clean branch, posting Copilot requests, merging, closing, and commenting.
- The worker SHOULD keep the default pull request queue read lightweight, use a lighter repo-wide metrics read for the summary cards, keep a separate cached full-summary path for filtered views, and invalidate those caches after pull request mutations.
- When resolving a Paperclip issue for a pull request, the worker SHOULD first match GitHub issues referenced in
closingIssuesReferencesto imported Paperclip issues and only fall back to pull-request-created issue links when no closing-issue match exists. - The
sync.runNowaction SHOULD support a prompt-running-state response for callers that passwaitForCompletion: false, but built-in UI and scheduled-job callers MUST wait for completion so nested Paperclip host service calls keep their invocation scope for the full sync. - A manual sync requested from a company-scoped settings or dashboard view MUST only sync repository mappings for that company.
- The worker SHOULD support targeted manual sync requests for a specific mapped Paperclip project, imported Paperclip issue, or Paperclip issue linked directly to a GitHub issue or pull request.
- The plugin MUST declare a scheduled job that ticks every minute and only performs a scheduled sync when the saved frequency is due.
- The plugin MUST expose agent tools for the GitHub issue and pull request workflow around synced work, including repository-item search, issue reads and updates with GitHub-native
completed,not_planned,duplicate, andreopenedstate reasons,assign_to_current_userfor assigning issues to the GitHub user authenticated by the saved token, issue comment reads and writes, pull request creation and updates, pull request asset upload, pull request file and CI inspection, review-thread reads and replies, review-thread resolution changes,request_pull_request_reviewersreviewer requests, organization-level GitHub Project search/listing, associating pull requests with organization-level GitHub Projects, andlink_github_itemfor linking a Paperclip issue to a GitHub issue or pull request in mapped or third-party repositories. - Agent tools that send non-empty freeform GitHub body content, such as issue bodies, pull request descriptions, comments, or review-thread replies, MUST append a GitHub-flavored Markdown footer that uses a horizontal rule plus a compact heading to disclose that a Paperclip AI agent created the message. When the caller provides an
llmModel, that footer MUST also include which LLM was used. - The plugin MUST expose an agent-authenticated native plugin JSON API route for recording Paperclip-attributed pull request metric events, and
create_pull_requestMUST automatically record a pull-request-created metric event when it succeeds. create_pull_requestMUST be the single agent-facing operation for branch publication and pull-request creation. It MUST require a Paperclip issue id, plain local head branch name, exact full local branch-tip commit SHA, base branch, and PR title.create_pull_requestMUST assert issue checkout ownership and resolve the issue's trusted execution workspace plus company-scoped GitHub secret inside the plugin worker. It MUST NOT use the project primary workspace, require or return an agent-visible GitHub token, or expose a separate agent-facing branch-push tool.- Before creating the PR,
create_pull_requestMUST verify that the issue and execution workspace belong to the calling run company and project; the issue is checked out by the calling agent/run; any requested local checkout path resolves within the trusted execution workspace; the requested branch is checked out in that selected Git checkout; both checkoutHEADand the local branch tip equal the supplied commit SHA; the head differs from the base; and the commit descends from the fetched remote base. An explicit target repository MAY be outside Paperclip repository mappings and MAY differ from the checkout's configured remotes; when omitted, the tool MUST infer the repository only from an unambiguous project mapping. It MUST publish only the exact commit to the exact head branch without force, verify the remote SHA, and only then call GitHub's PR API. GitHub token permissions are the authority for whether publication and PR creation are allowed. - Branch publication MUST use a sanitized Git execution context that does not execute repository hooks or honor agent-controlled credential helpers, remotes, proxies, or arbitrary refspecs. Errors and logs MUST redact the resolved token.
- After successful PR creation,
create_pull_requestMUST persist a pull-request link entity for the supplied Paperclip issue so scheduled/manual sync can reconcile that issue from PR status even when the PR does not close a synced GitHub issue. create_pull_requestMAY accept optionallabels,userReviewers, andteamReviewers. The worker MUST apply them only after the pull request exists, MUST NOT undo or fail the creation when those follow-up calls fail, and MUST report their outcome in the result aslabels,requestedReviewers,requestedTeams, orwarnings.- After persisting the pull-request link,
create_pull_requestMUST append the new pull request to the issue's issue-link record (linkedPullRequestNumbersandlinkedPullRequests) so issue detail surfaces show it before the next sync. Issue-link record writes from tools and from sync MUST be serialized per company and Paperclip issue so concurrent writers cannot drop a link. update_pull_requestMAY accept a replacementlabelsset. When provided, the pull request MUST end with exactly those labels; an empty array clears them.get_issueMUST return the GitHub issueauthor(login, plus url and association when available) so agents can route reviewer requests to the reporter.get_issueMUST also returnparticipants(the author plus each distinct comment author with role, repository association, and bot flag) andreviewerCandidates(the non-bot participants whose association is owner, member, or collaborator) so agents can request the reporter and the repository members who commented on the issue as pull request reviewers. When comments cannot be listed, the tool MUST still return the author-only participants and aparticipantsWarning.- Every pull-request link MAY store an optional company-scoped
followThroughAssigneeAgentId.create_pull_request,link_github_item, the authenticated pull-request metric route, and hosted PR-link actions MUST accept the field explicitly where they create or update a PR link and MUST use the same validation/persistence logic. An omitted property MUST preserve the current owner, an explicitnullMUST clear it, and a successful response SHOULD expose the persisted owner plusfollowThroughAssigneeUpdatedAtwhen present. - Before storing a non-null follow-through owner, the worker MUST resolve the agent through the authoritative host agent service and verify that it exists in the authenticated company. Missing or cross-company ids MUST fail closed before the link mutation. Owner changes through audited agent tools MUST remain represented in the existing issue-interaction intent/result ledger, and the durable link timestamp MUST make the current assignment change observable.
- The follow-through owner MUST survive ordinary PR title/state refreshes, worker process restarts, state reload, and execution-state settlement or cleanup. Pull-request link records written before this field existed MUST continue to normalize and sync without migration; their assignment behavior MUST remain the existing configured fallback behavior.
- Pull-request agent tools invoked with
paperclipIssueIdMUST resolve those direct pull-request link entities as well as pull requests discovered through a linked GitHub issue. When multiple direct links have the same pull-request number in different repositories, the caller MUST supply a matching repository; the worker MUST reject ambiguous or unlinked selections. create_pull_requestMUST be retry-safe across partial outcomes. If GitHub reports that a PR already exists after exact-SHA publication, the worker MAY reuse an open PR only when repository, head branch, base branch, and head SHA all match, then MUST reconcile the durable link and metric. It MUST NOT delete branches or close PRs as automatic compensation.- That API route MUST accept
pull_request_created, MUST use the host-provided authenticated agent company as the metric company, MUST reject any payloadcompanyIdthat disagrees with that company, and MUST deduplicate repeated events using a stable pull-request identity or an explicit event key. - When that API route receives a Paperclip issue id, it MUST verify the live pull request and persist the same pull-request link entity that
create_pull_requestwrites, so PRs created throughghor another non-plugin client still feed future issue status sync. - That API route MUST rely on the Paperclip host's
api.routes.registerauthentication and company access checks, so missing, invalid, expired, non-agent, or cross-companyPAPERCLIP_API_KEYrequests are rejected before worker dispatch. - The plugin MUST expose
link_github_itemas the agent-facing way to link a Paperclip issue to a GitHub issue or pull request. The tool MUST use the authenticated tool run company, MUST verify the live GitHub item, and MUST persist the same durable issue-link or pull-request-link entity that hosted manual link actions write. - The plugin MUST expose
upload_pull_request_assetas the agent-facing way to upload pull request assets. The tool MUST accept arbitrary base64 content or data URLs, including images and PDFs, MUST enforce the asset size limit, MUST write the asset to a non-merge artifact branch such aspaperclip-artifacts-pr-<number>, and MUST return an immutable raw GitHub URL plus Markdown suitable for the PR description. Image assets SHOULD return image Markdown, while non-image assets SHOULD return standard Markdown links. - The sync flow MUST fetch open GitHub issues from every configured repository.
- The sync flow MUST also include company-scoped GitHub issue and pull request link entities whose repositories are not covered by a configured mapping, and for those third-party repositories it MUST fetch only the linked GitHub issue or pull request instead of importing or scanning the entire repository.
- Full company and global sync runs MUST persist a company backlog snapshot using the current open GitHub issue count for each fully planned company mapping.
- When sync observes an already-imported GitHub issue move from open to closed, it MUST record a company-scoped closed-issues KPI event exactly once for that transition.
- The sync flow MUST ignore GitHub issues whose author username matches a configured ignored username for that mapping company.
- The sync flow MUST create one top-level Paperclip issue per imported GitHub issue when the target mapping has a resolved Paperclip project identifier.
- When the Paperclip runtime exposes plugin issue creation, the sync flow SHOULD prefer
ctx.issues.create(...)for imported issue creation and reserve direct Paperclip REST issue calls for repair or update paths so imported issues are not attributed to the connected board user. - Imported GitHub issues MUST be created with a GitHub Sync namespaced plugin origin and the canonical GitHub issue URL as
originId. - Imported GitHub issues MUST be created in neutral
backlogstatus without a configured assignee so issue creation itself does not activate work before durable recovery state exists. The namespaced origin and canonicaloriginIdMUST be written atomically with creation so a restart can recover the same issue without duplication. - When a Paperclip issue has a GitHub Sync GitHub-issue or pull-request
originKindand a parseable GitHub URL inoriginId, issue-scoped GitHub detail reads MUST use that origin metadata as a durable fallback when plugin-owned link entities, import-registry entries, or hidden description markers are missing. - After a new or origin-recovered import has durable activation state, the sync flow MUST apply the configured default status and assignee. When activation requires an agent wake, its intent MUST be persisted before status or assignment mutation and retried with the same episode identity after interruption.
- Imported Paperclip issues MUST keep the original GitHub issue title without adding a
[GitHub]prefix. - Imported issue descriptions SHOULD contain the normalized GitHub body when present and MUST normalize the GitHub raw HTML constructs that Paperclip cannot render in multiline descriptions.
- Imported issue descriptions MUST also retain a machine-readable hidden marker for the source GitHub issue URL so agents and repair paths can still recognize imported issues when plugin-owned entity or registry state is missing.
- GitHub repository, issue, PR, label, and sync metadata SHOULD move into a dedicated issue detail surface instead of being prepended into the issue description.
- Repeated sync runs MUST continue reconciling imported Paperclip issue descriptions against the latest GitHub issue body.
- Direct Paperclip REST calls from manual setup or sync actions MUST use the current browser origin by default. When the GitHub Sync settings page's optional Worker Paperclip API URL field is filled, the settings UI MUST persist it into plugin config
paperclipApiBaseUrland the worker/action path MUST use that configured origin instead of browser origin. - The worker MUST treat the plugin-configured Paperclip API origin as the trusted source for direct authenticated Paperclip REST calls, and MUST reject ad hoc action inputs that point at a different origin.
- The worker MUST NOT rely on a Paperclip process environment variable named
PAPERCLIP_API_URL; release-target e2e verification uses an explicit Worker Paperclip API URL because plugin worker runtime environments do not guarantee that process variable. - Direct Paperclip REST fetch failures MUST preserve method, URL, primary error, nested cause message, and cause code in diagnostics when available so TLS and routing problems remain actionable.
- Before a manual or scheduled sync touches any mapping whose company is missing board access, the worker MUST probe
/api/healthon the resolved Paperclip API origin and fail fast with configuration guidance when that deployment reportsdeploymentMode: "authenticated". - When a company has connected Paperclip board access, the worker MUST attach
Authorization: Bearer <board-token>to direct Paperclip REST issue and label calls for that company, using the saved secret ref when resolvable and the company-scoped worker-local board-token fallback when plugin secret refs are disabled. - When the Paperclip runtime exposes existing issue labels for the target company, the sync flow MUST map GitHub labels onto matching Paperclip labels by name and SHOULD prefer an exact color match when multiple Paperclip labels share the same name.
- When no matching Paperclip label exists and the local Paperclip label API is reachable, the sync flow MUST create the missing Paperclip label using the GitHub label color when available before attaching it to the imported issue.
- When a local Paperclip REST call returns an unexpected non-JSON success payload, such as an authenticated HTML sign-in page, the worker MUST treat that response as unavailable, fall back to SDK-based issue mutation when possible, and surface an actionable sync error that points operators to Paperclip board access or the Worker Paperclip API URL setting when labels still cannot be reconciled.
- Repeated sync runs MUST skip recreating issues that were already imported for the same mapping.
- If the plugin-owned import registry is stale or missing, repeated sync runs MUST repair deduplication by reusing an existing imported Paperclip issue in the mapped project when its GitHub Sync origin metadata, durable GitHub link metadata, or, for legacy issues, description source link matches the canonical GitHub issue URL.
- When a linked GitHub issue is transferred to another repository, sync MUST detect the canonical issue URL returned by GitHub. If the destination repository is mapped to a Paperclip project in the same company, sync MUST move the existing Paperclip issue to that project, update the active GitHub link metadata and import-registry row, and avoid creating a duplicate Paperclip issue. If the destination repository is not mapped, sync MUST unlink the Paperclip issue from GitHub and transition it to
cancelledwith an explanatory Paperclip comment. - The worker SHOULD persist pull request to Paperclip issue links in a plugin-owned entity so project-scoped queue and detail surfaces can reuse durable links without reparsing issue descriptions.
- Repeated sync runs MUST continue reconciling imported Paperclip issue statuses against the latest GitHub state.
- Repeated sync runs MUST also monitor pull requests that were linked to Paperclip issues through the project Pull Requests page, the
create_pull_requestagent tool, thelink_github_itemagent tool, the agent-authenticated metric API route, or the manual issue-page fallback when those pull requests do not close a synced GitHub issue, and MUST reconcile those Paperclip issue statuses from the pull request CI, merge, review decision, review-thread state, and terminal merge/closed state. - For Paperclip issues linked directly to pull requests, open linked PRs MUST continue to drive the active/review status from CI, mergeability, review decision, and review-thread state. When all directly linked PRs are terminal, any merged linked PR MUST map the Paperclip issue to
done; only closed-unmerged linked PRs MUST map it tocancelled. - Direct pull-request sync MUST persist bounded per-PR top-level and review-comment watermarks. A newly observed comment from that pull request's author or a verified repository maintainer/admin MUST re-arm non-backlog work even when the effective PR fingerprint is unchanged; untrusted comments MUST advance the observation watermark without re-arming the issue.
- Direct pull-request links MUST act as fallback status drivers only for Paperclip issues that are not also linked to a GitHub issue. When the same Paperclip issue is linked to a GitHub issue, the live GitHub issue snapshot and its closing pull-request references MUST own the status decision so stale direct pull-request links cannot terminalize still-open GitHub issue work.
- Repeated sync runs MUST continue reconciling Paperclip issues linked directly to third-party GitHub issues from the live GitHub issue state, labels, description, linked pull requests, and trusted comment rules without requiring that GitHub repository to be mapped to a Paperclip project.
- When the local Paperclip host API is available, sync-driven Paperclip status transitions SHOULD go through the same issue-update path Paperclip UI uses so timeline activity is recorded for agents and humans.
- Repeated sync runs MUST continue reconciling imported Paperclip issue labels against the latest mapped GitHub labels, including removing labels that were removed on GitHub.
- An open GitHub issue without a linked PR that was created by a repository maintainer/admin MUST map to Paperclip
todowhen it is first imported. - An open GitHub issue without a linked PR MUST otherwise map to the configured default Paperclip status when it is first imported, and that default MUST be
backlogwhen the company has not chosen another status. - If a real Paperclip host creates a newly imported maintainer-authored issue in
backlogbefore first-sync reconciliation promotes it totodo, that promotion MUST preserve the imported default assignee instead of applying the configured executor handoff assignee. - When a newly imported GitHub issue finishes sync in Paperclip
todoand is assigned to an agent, the worker MUST best-effort enqueue an assignment wakeup for that assignee using the imported Paperclip issue as wake context, preferringctx.issues.requestWakeup(...)and only falling back to the local Paperclip wakeup route when the SDK path is unavailable or rejected before dispatch. - When sync moves a Paperclip issue into
in_review, the worker MUST prefer the current reviewer or approver from the issue execution policy and execution state when Paperclip exposes that participant. If a visible internal review or approval stage exists but Paperclip does not expose the participant yet, the worker MUST fall back to the configured reviewer or approver handoff assignee when one is saved. If no internal review or approval stage is visible and the transition is only waiting on a healthy linked pull request, the worker MUST leave the issue unassigned instead of waking an internal reviewer or approver. An execution state explicitly markedcompletedMUST be treated as historical terminal evidence even when completed-stage, final-decision, or return-assignee fields remain populated; when an open healthy PR restores such an issue fromdonetoin_review, the worker MUST atomically clear the completed execution policy/state and the agent assignee and MUST NOT request an assignment wakeup. - When sync moves a Paperclip issue back into active execution (
todoorin_progress), assignment precedence MUST be: an active issue execution-statereturnAssignee; the actionable directly linked pull request's durablefollowThroughAssigneeAgentId; the configured executor handoff assignee; then the default imported assignee. A completed/settled execution state MUST NOT shadow the durable PR owner. If the stored owner no longer resolves to an agent in the company, sync MUST ignore it and continue through the configured fallbacks. - A tombstoned (
unlinked) GitHub issue link record MUST NOT count as an existing import. Unlinking is how an operator detaches a Paperclip issue from a GitHub issue; the registry-repair lookup MUST honour it, so the next sync imports a fresh Paperclip issue instead of re-adopting the detached one. - Once sync has moved an issue out of
blocked, it MUST NOT do so again until the externally observable state of the linked pull requests changes. The worker MUST persist a hash of that state (repository, number, head SHA, CI state, mergeability, merge state status, review decision, unresolved-review-thread flag) at the moment it leavesblocked, and MUST preserveblockedwhile the current hash equals it.blockedis a deliberate decision by an agent or a human; sync owns changes in GitHub, not steady state. An issue with no linked pull request has no external state and MUST never be held back by this rule. - A directly linked PR with red CI, an actionable unresolved review thread, a merge conflict or other action-required merge state, or trusted follow-up MUST assign and wake its selected durable owner when that owner wins the precedence above. A healthy external-maintainer wait MUST remain unassigned even when its link has an owner. The owner MUST remain on the link when execution state is cleared or settled.
- Unassigning an issue for a maintainer wait means removing the agent assignee only. When the issue carries a human assignee, the worker MUST keep it: a person parked on an
in_reviewissue is the reviewer the work is waiting on, not stale agent routing. The worker MUST write that preserved human assignee into the same patch it uses for the transition, so a settled maintainer wait is recognised as already applied and is not re-patched (or re-ledgered) on every sync pass. - Configured default, reviewer, approver, and executor handoff assignees MAY target either a Paperclip agent or the connected board user for that company.
- When sync moves an assigned issue into an actionable agent-owned state because of a sync-driven status transition, the worker MUST best-effort enqueue an explicit assignment wakeup for that assignee using the imported Paperclip issue as wake context, preferring
ctx.issues.requestWakeup(...)and only falling back to the local Paperclip wakeup route when the SDK path is unavailable or rejected before dispatch. - After successfully applying any GitHub-derived status, the worker MUST immediately persist a bounded fingerprint of the effective issue/PR condition before dispatching a required wake. The effective fingerprint MUST cover identity, head SHA, issue/PR closure outcome, and the highest-priority actionable check/review/merge condition, MUST cover directly PR-linked issues and terminal
done/cancelledactions, and MUST NOT change for comment counts or transient rawUNKNOWN/aggregate jitter that leaves the effective action unchanged. An unchanged effective fingerprint MUST suppress repeated transitions, explanatory comments, and assignee wakes only when local Paperclip status, assignment, and execution metadata already match the desired state. Local drift under unchanged remote evidence MUST be corrected through a drift-specific journal attempt, including one required wake when the corrected state is actionable and agent-owned. Changed effective fingerprint evidence and trusted new issue, pull-request, or review-thread comments MUST bypass suppression. Failed status actions MUST NOT advance trusted-comment watermarks. A failed required wake MUST remain durably pending and retry without repeating the already successful status/comment mutation. - Every sync-driven transition MUST use a fixed-length semantic action identity that includes the Paperclip issue, target state, effective remote action, and trusted-comment watermarks. The worker MUST durably journal separate comment and issue-mutation intents/results, reuse completed phases on retry, keep the first intent timestamp stable, and fail closed when an intent has no trustworthy result. It MUST NOT repeat a transition comment or issue mutation merely because final result persistence or a later wake failed.
- If an imported Paperclip issue is currently
backlogand its linked GitHub issue is still open, the sync flow MUST preservebacklog; only a manual Paperclip transition may move it out ofbacklog. - If a Paperclip issue that came from an open GitHub issue without a linked PR is later moved out of
backlog, the sync flow SHOULD preserve that Paperclip status until another open-issue GitHub rule applies. - If that Paperclip issue is currently
doneorcancelledwhile the linked GitHub issue is open with no linked PR, the sync flow MUST move it totodoinstead ofbacklogso it re-enters the active queue. - An open GitHub issue with a linked PR that still has unfinished CI jobs MUST map to Paperclip
in_progress, except when the Paperclip issue is alreadyblockedand the only adverse PR signal is pending external merge requirements such as GitHubBLOCKEDorUNSTABLE; in that case sync MUST preserveblockedinstead of waking an executor. - An open GitHub issue with a linked PR that has red CI jobs, an action-required GitHub merge state such as
CONFLICTING,DIRTY,BEHIND,BLOCKED,DRAFT, orUNSTABLE, or unresolved review threads MUST map to Papercliptodo, unless the worker can route the issue back to an executor through execution-policy or configured handoff data, in which case it MUST map toin_progress. - An open GitHub issue with a linked PR that has green CI, a review-ready GitHub merge state such as
CLEANorHAS_HOOKS, and all review threads resolved MUST map to Paperclipin_review; aggregate GitHubCHANGES_REQUESTEDalone MUST NOT move that wait back to active execution without an actionable unresolved thread, failing or pending CI, action-required merge state, draft state, or trusted new comment. - An imported Paperclip issue that is currently
blockedand has any non-terminalblockedByissue MUST remainblockedduring GitHub Sync even when the linked GitHub issue or PR otherwise looks review-ready. - An open GitHub issue with a linked PR in GitHub merge state
UNKNOWNMUST NOT be treated as review-ready on first import or while the issue is already in active execution. When an imported Paperclip issue is alreadyin_review, or is being reopened fromdonewith a linked PR, transientUNKNOWNmergeability MUST preserve or restorein_reviewif CI is green, all review threads are resolved, and no trusted new comment, draft state, explicit action-required merge state, or unresolved first-class blocker applies. - Linked pull requests MAY live in a different GitHub repository than the linked issue, and the worker MUST evaluate CI, review-thread state, stored link metadata, and rendered GitHub deep links against each pull request's actual repository instead of assuming the issue repository.
- A closed GitHub issue completed as finished work MUST map to Paperclip
done. - A closed GitHub issue closed as not planned or duplicate MUST map to Paperclip
cancelled. - When sync moves an imported Paperclip issue into
doneorcancelled, or sees an already-terminal imported issue whose GitHub source is still terminal, it MUST clear any pending Paperclip review or approval execution policy/state as part of that same reconciliation so the host does not reject the terminal status update or keep waking stale review participants. - A new GitHub comment on an open imported issue MUST move the corresponding Paperclip issue back into active work only when at least one newly added comment since the last sync was written either on the source GitHub issue, in a linked pull request's top-level comment stream, or in a linked pull request review thread by the original GitHub issue author or by a repository maintainer/admin that the worker can verify through the GitHub API; that active-work status MUST be
in_progresswhen the worker can route the issue back to an executor through execution-policy or configured handoff data andtodootherwise. - A new GitHub comment from any other GitHub account MUST NOT move the corresponding Paperclip issue back into active work.
- If that Paperclip issue is currently
backlog, trusted GitHub comments MUST still leave it inbacklog. - Whenever the sync flow changes a Paperclip issue status, it MUST add a Paperclip issue comment that explains the old status, the new status, and the GitHub condition that caused the transition.
- The worker MUST append sanitized issue-scoped interaction events as
paperclip-github-plugin.issue-interaction-evententities for sync status decisions (including no-ops and direct-PR-driven transitions) and issue-scoped mutating agent tool attempts. Before a mutation, it MUST persist a uniquely identified intent event; if that write fails, it MUST NOT execute the mutation. Concurrent identical mutation attempts MUST retain distinct logical attempt identities. It MUST persist a separate result event afterward, including a failed result for known mutation/comment/annotation failures. Intent/result persistence failures MUST return structured tool errors rather than rejecting the tool invocation; a result-write failure after a failed mutation MUST surface both failures. Only structured:intentevents without a matching:resultevent MUST be reported as uncertain attempts rather than successful writes. Each entity MUST use a content-addressedexternalId, keep the logical dedupe key in sanitized event data, preserve company/issue plus available agent/run attribution, and retain normalized remote identifiers resolved from authoritative Paperclip issue/PR links, derivable from repository/number,reference, orpullRequestUrlinputs, or returned by newly created GitHub items. It MUST NOT persist raw comment/body content, logs, credentials, headers, secret refs, or provider payloads. Persisted HTTP(S) URLs MUST have userinfo, query strings, and fragments removed. Concurrent identical writes MUST converge without overwriting different content; conflicting content under one logical dedupe key MUST make ledger coverage incomplete. A sync status decision that reaches the same no-op conclusion from unchanged state as an already-settled attempt in the same interaction family MUST reuse that attempt's identity andoccurredAtso repeated passes converge on the stored events instead of appending a new intent/result pair each time; attempts that are still open, or that mutate, MUST take a new identity. - The plugin MUST expose
get_issue_interaction_summaryfor authenticated agents. The tool MUST verify the Paperclip issue in the authenticated run company, accept an optional maximum 30-day UTC[from,to)range, return deterministic bounded counts/transitions/repeat-and-reversal signals with dimensioned plugin-ledger/Paperclip-core/external-GitHub coverage and limitations, and perform no historical backfill. Malformed rows, conflicting logical keys, or a bounded ledger-scan cutoff MUST make plugin-ledger coverage incomplete and be reported in integrity metadata. - When the SDK supports comment annotations, the plugin SHOULD render the referenced GitHub issue and PR links as a comment annotation attached to those sync-created comments.
- Saving a mapping MUST create or reuse the target Paperclip project.
- Saving a company-scoped mapping from the settings page MUST create or reuse the target Paperclip project in that same company.
- When the settings page creates a new target Paperclip project, it MUST set
executionWorkspacePolicy.enabledtotrueandexecutionWorkspacePolicy.defaultModeto"isolated_workspace"so the project opts into isolated issue checkouts and new issues default to isolated checkout. - Saving a mapping for a Paperclip project that is already bound to the GitHub repository MUST reuse that existing project instead of creating a duplicate workspace binding.
- Saving a mapping MUST bind the GitHub repository URL to the Paperclip project workspace.
- Once a project has been created and linked, its project name field SHOULD be treated as read-only in the settings UI.
- The plugin MUST register successfully in Paperclip.
- The plugin manifest MUST NOT declare a strict
minimumHostVersionorminimumPaperclipVersiongate while current latest/development Paperclip hosts may report0.0.0during plugin upgrade. Required host surfaces MUST remain represented by manifest capabilities and guarded by worker fallbacks where possible, and the docs MUST still state the intended Paperclip2026.831.1support baseline. - Disposable e2e and manual host verification MUST pin the Paperclip CLI to
2026.831.1by default, while retaining an explicit environment override for forward and backward compatibility checks. - The plugin MUST expose a dashboard widget contribution for sync readiness and setup.
- The plugin MUST expose a separate dashboard KPI widget contribution.
- The plugin MUST expose a settings page contribution.
- The plugin SHOULD expose an issue detail contribution for GitHub metadata.
- The issue detail contribution SHOULD prefer the slot-provided issue context before falling back to hook-provided host context. It SHOULD hide itself only when the host is not rendering an issue-scoped surface; when it expects issue context but cannot resolve the canonical Paperclip issue id, it SHOULD render compact troubleshooting details instead of disappearing. When the current Paperclip issue has no linked GitHub issue or pull request, it SHOULD render a compact manual-link fallback instead of an inert empty state.
- The issue detail contribution SHOULD allow operators to manually link an unlinked Paperclip issue to either a GitHub issue or a GitHub pull request by entering either a number or a full GitHub URL. Number-only entries SHOULD resolve against the issue's mapped Paperclip project repository; full URLs SHOULD be allowed when the referenced repository is mapped to that project.
- A manual GitHub issue link for a mapped repository MUST write the same issue-link entity and import-registry entry used by normal GitHub issue imports. A manual GitHub pull request link MUST write the same pull-request link entity used by project Pull Requests page create/link flows.
- Agent tool links MAY target third-party repositories that are not mapped to the Paperclip issue's project. Those links MUST still write company-scoped durable issue-link or pull-request-link entities, and sync MUST use those entities as targeted worklist entries rather than requiring a repository mapping.
- The issue detail contribution SHOULD allow operators to unlink a linked Paperclip issue from GitHub. Unlinking MUST remove the link from active worker resolution, remove any matching import-registry entry, clear GitHub Sync origin targets and hidden import markers from the Paperclip issue where possible, and leave both the Paperclip issue and GitHub item intact.
- Agent-facing tools MUST NOT expose an unlink operation for issue-link or pull-request-link metadata. Only operator UI actions and internal sync repair paths such as an issue transfer to an unmapped repository may tombstone link metadata.
- The issue detail contribution SHOULD include GitHub-marked issue-scoped action buttons alongside the GitHub deep links so operators can refresh that imported issue from the same panel.
- The issue detail contribution SHOULD show the GitHub issue creator with a compact avatar treatment consistent with the pull request page.
- When a linked pull request belongs to a different repository than the linked issue, the issue detail contribution and sync-created comment annotations SHOULD render that pull request using its actual repository URL and a label that makes the cross-repository link clear.
- The plugin SHOULD expose a project sidebar item that opens a project-scoped Pull Requests page for the mapped repository and can show the current open pull request count for mapped projects through a lightweight count read instead of the heavier summary-card metrics path. Paperclip
2026.831hosts do not mountprojectSidebarItemcontributions (streamlined sidebar, PAP-12472); the contribution stays declared for hosts that render it, and the page route MUST remain reachable without it. - The project pull request sidebar count, page, and metrics reads SHOULD tolerate saved mappings that are missing either the company id or the project id when the active Paperclip project context still identifies the intended mapping safely, and they SHOULD also fall back to the active project's bound GitHub repository when no saved sync mapping exists but the project workspace already defines that repository.
- The project Pull Requests page SHOULD render live open pull request data for the mapped repository, including checks, an explicit up-to-date branch state, target branch badges, review summary, unresolved review-thread state, non-review comment counts, last-updated timestamps, Paperclip issue linkage, and quick actions.
- The project Pull Requests page SHOULD include an Up to date column that distinguishes pull requests already current with the base branch from pull requests that are behind but can be updated cleanly and pull requests that need conflict resolution, and it SHOULD render that status from explicit branch-comparison data when GitHub provides it; when GitHub cannot confirm branch freshness, the queue SHOULD show an unknown state instead of assuming the branch is current.
- The project Pull Requests page SHOULD expose a deterministic Update branch action for pull requests in the clean behind-base state, and it SHOULD keep conflict cases out of that direct path so they can be handled separately.
- The project Pull Requests page SHOULD expose a Copilot quick-action menu that posts
@copilotpull request comments for Fix CI on failing pull requests, Rebase on behind-with-conflicts pull requests, and Address review feedback on pull requests with unresolved review threads, while Review SHOULD use GitHub’s native reviewer-request flow for Copilot instead of a plain comment. - The project Pull Requests page SHOULD label the review-summary table column as Approvals, surface inline review, quick-approve, and quick-request-changes actions there when review submission is allowed, and keep approval, change-request, CI rerun, merge, close, and resolved-thread icon colors consistent with the corresponding queue and detail actions.
- The project Pull Requests page SHOULD only show a pull request action when the configured GitHub token has been verified to hold the permission required for that action on the mapped repository; when permission audit data is unavailable, the page SHOULD fail closed and hide the action instead of assuming access.
- The project Pull Requests page SHOULD surface total, mergeable, reviewable, and failing summary counts and let operators filter the table from those cards.
- The project Pull Requests page SHOULD only treat a pull request as mergeable when it targets the repository's current default branch, has passing checks, has at least one approval, has no outstanding changes-requested reviews, and has no unresolved review threads.
- The project Pull Requests page SHOULD keep KPI-triggered filtered views fast by reusing cached repo-wide filter indexes and fetching only the visible filtered rows instead of rebuilding every row summary on each click.
- The project Pull Requests page SHOULD favor repository-scoped caches for totals, KPI aggregates, and per-pull-request review or CI insights, and SHOULD expose an explicit refresh path that invalidates those caches on demand.
- The project Pull Requests page SHOULD paginate the queue in batches of 10 rows.
- The project Pull Requests page SHOULD keep the visible queue sorted by the most recently updated pull requests first.
- The project Pull Requests page SHOULD deep-link row icons to the relevant GitHub resource where possible.
- The project Pull Requests page SHOULD render markdown and only sanitized, allowlisted inline HTML in pull request descriptions and comments instead of showing raw author text.
- The pull request markdown renderer MUST strip event-handler attributes, inline styles, executable or embedded elements, and unsafe URL schemes, and SHOULD limit inline HTML support to non-executable formatting and link elements such as
a,code,em,strong,b,i,br,span,sub, andsup; allowed attributes SHOULD stay limited to safe link metadata such ashref,title, andtargetona, plusclassonspanwhere markdown-compatible formatting needs it. - The project Pull Requests page SHOULD include a compact inline comment composer in the bottom detail pane.
- The project Pull Requests page SHOULD support modal quick actions for commenting, reviewing, re-running CI, and confirming close actions in addition to merge when the pull request state allows them, while keeping Copilot requests as lightweight inline menus instead of separate modal flows.
- The review modal SHOULD support comment-only, approve, and request-changes submissions so operators can match GitHub’s review outcomes without leaving the queue.
- When GitHub rejects a pull request comment or review action, the worker and UI SHOULD preserve actionable permission or validation guidance instead of collapsing it to a generic error.
- When a pull request is linked to a Paperclip issue, the project Pull Requests page SHOULD open that issue in a plugin-provided right drawer so operators can stay on the queue page, while still allowing explicit navigation away when desired.
- When a pull request is not yet linked to a Paperclip issue, the project Pull Requests page SHOULD offer an inline create-issue action and wait for the returned Paperclip identifier before rendering the issue link or opening its drawer.
- Paperclip issues created from the project Pull Requests page MUST be created with a GitHub Sync namespaced plugin pull-request origin and the canonical GitHub pull request URL as
originId. - Paperclip issues created from the project Pull Requests page MUST explicitly start in
todoand use Paperclip's standard work mode so host defaults for backlog parking or planning-mode work cannot change the queue semantics. - The settings page SHOULD audit the saved GitHub token against the mapped repositories in the active company and SHOULD warn when required pull-request-action permissions are missing or GitHub cannot verify them yet.
- The plugin SHOULD expose manual sync buttons in the global toolbar and on mapped project/issue surfaces when the host renders those slot types.
- The sync dashboard widget MUST summarize the current GitHub sync readiness and link to setup.
- The separate KPI dashboard widget MUST show company KPI cards for open GitHub backlog, GitHub issues closed, and Paperclip pull requests created, including recent trend history and comparison against earlier periods when data exists.
- When the latest sync run records issue-level failures, the settings page SHOULD expose a saved failure log with per-failure repository, issue, phase, raw error, and suggested next-step details, and compact surfaces SHOULD still surface at least the latest saved failure snapshot.
- The settings page MUST render inside the real Paperclip host.
- The plugin MUST include end-to-end automation that boots a disposable Paperclip instance, installs the plugin, and verifies the settings page renders.
- The build pipeline MUST bundle the manifest and worker for Node execution and bundle the hosted UI separately for browser execution.
- Local development SHOULD provide a watch-mode build and a local UI dev server for hosted-UI iteration.
- The build pipeline MUST stamp the plugin manifest version from build-time package metadata rather than keeping a separately hardcoded manifest version.
- The release workflow MUST derive the published version from the GitHub release tag and stamp that version into publishable package metadata before build and publish.
- After a successful publish, the release workflow MUST sync that resolved release version back into the checked-in
package.jsonon the release target branch so repository metadata reflects the latest published release. - The release workflow MUST run on a Node.js version that already satisfies npm trusted publishing requirements instead of relying on an in-job npm self-upgrade step.
Paperclip 2026.831 makes plugin config company-scoped, re-enables plugin secret refs, routes agent tool execution through the host tool gateway, and adds optional capabilities that GitHub Sync does not need.
- Plugin config is company-scoped. The worker MUST pass the company id to
ctx.config.get(companyId)whenever a data, action, tool, job, or API-route path knows the company, MUST declaremultiCompanyConfig: truebecause one worker serves every company on the instance, and MUST implementonConfigChanged(newConfig, { companyId })by remembering the delivered config per company so proactive paths (scheduled sync, actions without a company) can still resolve company-keyed settings when the host cannot derive a scope. The worker MUST NOT read plugin config fromonHealth(), which has no company scope. Startup config passed tosetup()is empty; the worker MUST NOT depend on it. - Proactive worker-to-host calls (scheduled sync, webhook-free background work) are only authorized for companies that have a saved GitHub Sync plugin config row. When the host denies a company scope during a sync run, the worker MUST record a sync error that tells the operator to open GitHub Sync settings in that company and save once, instead of surfacing the raw host error.
- The settings UI MUST read and write plugin config with the active company (
GET /api/plugins/:id/config?companyId=andPOSTwithcompanyId), MUST write secret refs as{ "type": "secret_ref", "secretId" }bindings, and MUST keep the manifestinstanceConfigSchemaaccepting both the binding and the legacy bare-UUID string so config rows migrated from older releases keep validating until they are re-mirrored. - Agent tool discovery and execution go through the host tool gateway with the company tool-access policy. Tool names remain
<pluginId>:<tool>; the plugin MUST NOT assume its tools bypass that policy, and the KPI attribution endpoint remains a native plugin JSON route authenticated by the agent run token. - The host tool gateway is fail-closed: with no matching profile, grant or allow policy it denies by default, and plugin tools carry no connection or catalog id, so only
tool_nameorrisk_levelprofile entries (or a profile withdefaultAction: "allow") can grant them. The documentation MUST describe a working company-scoped tool profile recipe for all declared GitHub Sync tools. - The settings registration data MAY include a best-effort, read-only
agentToolAccesssummary reporting how many GitHub Sync tool names one sampled agent of the company can currently see. Because the host evaluates tool profiles per agent and agent-scoped bindings override company-scoped ones, the summary MUST identify the sampled agent and the settings page MUST present the warning as a single-agent sample rather than a company-wide verdict. The check MUST NOT fail settings loading: missing board access, missing agents, an unreachable worker Paperclip API URL, or a host that answers 403/404 MUST be reported as unavailable with anullcount instead of an error. - Company export/import bundles carry no plugin section, so GitHub Sync mappings, plugin config, link entities, import registry, KPI history and interaction ledger do not survive an import. The documentation MUST state this limitation; automatic re-linking of an imported company is out of scope for this release.
- The host's merged-PR confirmation sweep and the experimental GitHub MCP app definition MAY operate alongside GitHub Sync. GitHub Sync MUST remain the source of truth for mapped and linked Paperclip issue status, and MUST NOT delegate status routing to either surface.
- GitHub Sync MUST NOT declare
issue.interactions.read,issue.attachments.read,approvals.read,issue.comments.create_human_attributed,issue.interactions.respond, orapprovals.responduntil worker code actually uses those host surfaces; capabilities stay minimal and tied to real behavior. - The manifest MUST NOT declare a strict
minimumHostVersionorminimumPaperclipVersiongate; compatibility is expressed through capabilities and worker fallbacks. - The
2026.831streamlined main sidebar has noprojectSidebarItemmount point. The plugin MAY keep declaring that slot, MUST NOT depend on it for reaching the project Pull Requests page, and disposable e2e verification MUST open that page by route while only asserting the sidebar link when the host renders it.
The boundaries adopted with Paperclip 2026.626 still apply. Paperclip 2026.626 external object references, task watchdogs, ask work mode, routine date variables, and built-in Hermes adapters are host/runtime capabilities. GitHub Sync MUST NOT declare a duplicate GitHub external.objects.* provider while the host ships a built-in GitHub external-object provider. Because that host provider now supersedes the plugin's generic comment-link rendering, GitHub Sync MUST NOT declare the legacy commentAnnotation UI slot for GitHub links. Synced descriptions, comments, and status-transition annotations SHOULD continue to include canonical GitHub issue and pull request URLs so the host provider can detect them, but GitHub Sync's mappings, plugin entities, sync jobs, and agent tools remain authoritative for routing mapped/linked Paperclip issue state.
Task watchdogs MUST NOT replace GitHub Sync's scheduled/manual PR status refresh. GitHub Sync MUST NOT create, read, update, delete, or auto-attach /api/issues/:id/watchdog configuration, and MUST NOT pass a watchdog field when importing or creating GitHub-backed Paperclip issues. Waiting on GitHub CI, mergeability, review threads, or maintainer approval is an external GitHub wait, not a stopped Paperclip task tree. GitHub Sync MAY coexist with operator-created task watchdogs, and its normal issue comments/status transitions SHOULD remain visible to host watchdog reconciliation. However, GitHub Sync MUST continue to drive issue status from GitHub snapshots and MUST NOT treat watchdog review issues as the source of truth for PR state. GitHub Sync tools are normal company-scoped plugin tools, not currently task-watchdog-scope-aware; a future host/plugin contract is required before mutating GitHub tools can enforce watched-subtree scope.