Skip to content

feat: adopt Paperclip 2026.831 host surfaces (tool-access recipe, GITHUB_TOKEN exposure, boundary docs) - #204

Merged
alvarosanchez merged 6 commits into
mainfrom
feat/paperclip-2026-831-adoption
Sep 14, 2026
Merged

alvarosanchez merged 6 commits into
mainfrom
feat/paperclip-2026-831-adoption

Conversation

@alvarosanchez

Copy link
Copy Markdown
Owner

Implements the adoption items from the 2026.831 feature-fit analysis of GitHub Sync against Paperclip v2026.831.1.

What changed

1. Remove dead comment-annotation UI (439edfe)
The manifest stopped declaring the commentAnnotation slot when the host shipped its built-in GitHub external-object provider, so GitHubSyncCommentAnnotation, its data interface and the annotation-only styles were unreachable. The worker's comment.annotation data handler stays: it is still covered by tests and is the data source a host that renders the slot would read.

2. Tool-access profile recipe and detection (4c302ad)
The host MCP tool gateway is fail-closed. tool-access-policy.ts ends in deny_default when nothing matches, and plugin tool dispatch carries no connection/catalog/application id, so connection and catalog_entry profile selectors can never match a plugin tool (those selectors are validated to have a non-null id at write time). Only tool_name, risk_level or defaultAction: "allow" can grant them.

  • README "Granting the tools to agents": the 21 qualified tool names, the exact 2026.831.1 request/response shapes, and a ready-to-run curl/jq script that derives the names from GET /api/plugins/tools?pluginId=paperclip-github-plugin, creates a defaultAction: "deny" profile with one tool_name include entry per tool, binds it at company scope, and verifies against one agent.
  • Worker: settings.registration now returns a best-effort read-only agentToolAccess summary from GET /api/companies/{id}/tools/profiles/effective/agents/{agentId}, reporting toolsVisibleToAgents: number | null. Missing board access, missing agents, 403/404 and transport errors are reported as unavailable with a null count, never as an error. An allow-by-default (or risk_level) profile also reports null, because allowedToolNames only enumerates catalog-backed MCP tools and reporting zero there would be a false alarm. Cached two minutes per company.
  • UI: warning banner in the GitHub access section when the count is a definite zero, linking to the README section.

3. GITHUB_TOKEN exposure (20f002f)
server/src/services/git-credentials.ts probes company secrets named GITHUB_TOKEN, GH_TOKEN, PAPERCLIP_GITHUB_TOKEN for managed-checkout git auth, the merged-PR confirmation sweep, the execution-workspace reaper and the built-in GitHub external-object provider. GitHub Sync stores github_sync_<companyId>, so those host features ran unauthenticated.

New default-off checkbox "Also expose this token to Paperclip as GITHUB_TOKEN" creates or rotates that company secret with the same value on save. Request building lives in the new src/ui/host-secrets.ts so it is unit-testable. The host matches names with plain SQL equality, so the host secret is matched case-sensitively and a name/key conflict is surfaced with an actionable message rather than rotating the wrong row. A failure there never fails the token save.

4. Compatibility-boundary docs (deba72f)
Company export/import bundles have no plugin section at all, so mappings, plugin config, link entities, the import registry, KPI history and the interaction ledger do not survive an import (re-link action is future work). The experimental Connections v3 GitHub app is raw GitHub MCP tooling, whereas GitHub Sync tools are issue-aware, ledger-recorded and never expose the token. The host's merged-PR confirmation sweep is read-only and coexists with plugin status routing.

5. Tests (9367708)
12 new tests: six for the agentToolAccess worker data path, six for src/ui/host-secrets.ts.

Notes

  • The task description mentioned a selectorValue field on profile entries. There is no such field at v2026.831.1; the validator takes selectorType, effect, toolName, riskLevel, applicationId, connectionId, catalogEntryId, conditions, and silently drops unknown keys. The README documents the real shape.
  • SPEC.md gained matching requirements for the tool-access recipe, the agentToolAccess summary, the opt-in host secret, and the portability/coexistence boundaries.
  • No version bump, no release.

Verification

pnpm typecheck, pnpm test (350 pass, 0 fail) and pnpm build are green locally. pnpm test:e2e runs on merged main.

🤖 Generated with Claude Code

alvarosanchez and others added 5 commits September 14, 2026 15:53
The manifest no longer declares the `commentAnnotation` UI slot (SPEC
2026.626 boundary: the host ships a built-in GitHub external-object
provider), so the exported component, its data interface and the
annotation-only styles were unreachable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paperclip 2026.720+ routes plugin tool discovery through the MCP tool
gateway, whose policy service ends in `deny_default`, so a company with no
matching tool profile offers agents zero `paperclip-github-plugin:*` tools.
Plugin tool dispatch carries no connection/catalog/application id, so only
`tool_name` or `risk_level` profile entries (or `defaultAction: "allow"`)
can grant them.

- README: new "Granting the tools to agents" section with the 21 qualified
  tool names, the exact create/bind request shapes at 2026.831.1, and a
  ready-to-run curl/jq recipe that derives the names from
  `GET /api/plugins/tools?pluginId=paperclip-github-plugin`.
- worker: `settings.registration` now carries a best-effort read-only
  `agentToolAccess` summary built from
  `GET /api/companies/{id}/tools/profiles/effective/agents/{agentId}` for
  one checkable agent, reporting `toolsVisibleToAgents: number | null`.
  Missing board access, missing agents, 403/404 and transport errors are
  reported as unavailable with a null count, never as an error. Cached for
  two minutes per company and invalidated on config change.
- ui: warning banner in the GitHub access section when the count is a
  definite zero, linking to the README recipe.
- Split `resolvePaperclipApiAuthTokenForCompany` out of
  `resolvePaperclipApiAuthTokens` so a single-company probe can reuse it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…THUB_TOKEN

Paperclip's own GitHub features resolve a company secret by name, probing
`GITHUB_TOKEN`, `GH_TOKEN` and `PAPERCLIP_GITHUB_TOKEN`
(`server/src/services/git-credentials.ts`). They back managed-checkout git
auth, the merged-PR confirmation sweep, the execution-workspace reaper and
the built-in GitHub external-object provider. GitHub Sync stores its token
as `github_sync_<companyId>`, so those host features ran unauthenticated
unless the operator added a second secret by hand.

- New `src/ui/host-secrets.ts` holds the company-secret request builders and
  `exposeGitHubTokenToPaperclipHost`, extracted from the settings page so the
  request-building logic is testable.
- The token form gets a default-off checkbox "Also expose this token to
  Paperclip as GITHUB_TOKEN". When ticked, saving creates or rotates the
  `GITHUB_TOKEN` company secret with the same value. A failure there is
  reported as a toast and never fails the token save itself.
- The host compares secret names with plain SQL equality, so the host secret
  is matched case-sensitively and a name/key conflict is surfaced with an
  actionable message instead of rotating the wrong row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three host surfaces that operators keep hitting, verified against
`v2026.831.1`:

- Company export/import bundles have no plugin section at all
  (`packages/shared/src/validators/company-portability.ts`), so mappings,
  plugin config, link entities, the import registry, KPI history and the
  interaction ledger do not survive an import. A re-link action is future
  work.
- The experimental Connections v3 GitHub app exposes GitHub's hosted MCP
  server, i.e. raw `owner/repo` tools, while GitHub Sync tools are
  issue-aware, ledger-recorded and never expose the token.
- The host's merged-PR confirmation sweep is read-only and resolves human
  confirmation interactions; GitHub Sync stays the source of truth for
  mapped and linked issue status.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Worker data-path tests for `settings.registration.agentToolAccess`: the
skip path without board access, the counted path, the actionable zero, the
allow-by-default profile that must report an unknown count instead of zero,
silent tolerance of 403/404, and the no-agents case. Plus unit tests for
`src/ui/host-secrets.ts` request building, the case-sensitivity split
between plugin-owned and host-probed secret names, create vs rotate, and
the name-conflict message.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 14, 2026 16:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Six unresolved moderate findings affect tool-access reporting, host-secret handling, company switching, and setup-script error handling.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adopts Paperclip 2026.831 host integrations for GitHub Sync, including tool access, optional GITHUB_TOKEN exposure, and compatibility documentation.

Changes:

  • Adds tool-access detection, warnings, and setup guidance.
  • Adds opt-in host-secret token exposure.
  • Removes obsolete annotation UI and updates specifications, tests, and boundary documentation.
File summaries
File Summary Final findings
tests/plugin.spec.ts Adds worker and host-secret helper tests. No final comments.
src/worker.ts Adds cached agent tool-access probing. Moderate (2 votes): Sampling agents can misrepresent agent-scoped policies; evaluate relevant agents or label the result as a sample.
src/ui/index.tsx Adds token exposure control and access warnings. Moderate (1 vote): The warning may incorrectly claim company-wide denial when agent-scoped bindings differ. Moderate (1 vote): Reset or key the exposure opt-in when switching companies.
src/ui/host-secrets.ts Implements host-secret creation and rotation helpers. Moderate (3 votes): Trimming stored names conflicts with exact host matching. Moderate (1 vote): Inactive or archived secrets may be rotated without becoming usable.
SPEC.md Adds requirements for host integrations and compatibility boundaries. No final comments.
README.md Documents tool grants, token exposure, and compatibility. Moderate (2 votes): The setup script should fail on HTTP errors before consuming returned IDs.
Review details

Suppressed comments (3)

src/ui/host-secrets.ts:104

  • This path rotates any matching row without checking its status. If an existing GITHUB_TOKEN secret is disabled or archived, the documented host credential resolver skips it, so the checkbox can report success while Paperclip's host features remain unauthenticated. Include the status in the list result and reject or explicitly reactivate inactive rows before claiming exposure succeeded.
  if (existing) {
    const rotateRequest = buildCompanySecretRotateRequest(existing.id, value);
    return fetchJson<CompanySecretSummary>(rotateRequest.url, rotateRequest.init);

src/ui/index.tsx:12711

  • The probe requests effective profiles for only the first active/checkable agent, but this copy claims that no profile in the company includes the tools and that all agents will be denied. An agent-scoped binding for another agent can coexist with a zero result here (the README documents that agent bindings override company bindings), so the warning becomes false; either check all relevant agents or scope the summary and copy to checkedAgentId.
                      {`Paperclip's tool gateway is fail-closed, and no tool profile in this company includes any of the `}
                      {agentToolAccess?.totalToolCount ? `${agentToolAccess.totalToolCount} ` : ''}
                      {`${GITHUB_SYNC_PLUGIN_ID}:* tools, so agents will not be offered them. `}
                      {'Bind a tool profile with tool_name include entries for this company.'}

src/ui/index.tsx:11251

  • This opt-in flag is not reset when settings.data changes. Because the settings component survives company switches, a user can check it for company A, switch to company B, and then save B's token with host-secret exposure still enabled even though they never opted in for B. Reset it on company/settings transitions (or key the state by company) before allowing the next save.
  const [exposeTokenAsHostSecret, setExposeTokenAsHostSecret] = useState(false);
  • Files reviewed: 6/6 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread README.md Outdated
Comment thread src/ui/host-secrets.ts Outdated
Comment thread src/worker.ts
- README recipe: `curl --fail-with-body` plus a `require_id` guard so a 409
  or 403 cannot be parsed into a null id and used in the next request.
- host-secrets: the exact-match branch no longer trims the stored name, so a
  row named `GITHUB_TOKEN ` is not mistaken for the byte-exact name the host
  probes for; and a `GITHUB_TOKEN` secret the host reports as non-active is
  refused with an actionable message instead of being rotated into a state
  Paperclip silently ignores.
- agentToolAccess is now explicitly a single-agent sample: the summary
  carries `checkedAgentName` and `checkableAgentCount`, the docs and SPEC
  say so, and the settings banner names the agent it checked and explains
  that agent-scoped bindings override the company one.
- The host-secret exposure opt-in resets when the settings page switches
  company, so a ticked box cannot follow the operator into another company.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@alvarosanchez

Copy link
Copy Markdown
Owner Author

All six Copilot findings are addressed in 1988fa2. The three inline threads have replies and are resolved; the three that only appeared in the file-summary table:

  • src/ui/index.tsx — warning may claim company-wide denial. Same root cause as the worker finding: the banner is now titled after the sampled agent and states that it is a sample of one agent of N, and that an agent-scoped binding overrides the company one.
  • src/ui/index.tsx — reset or key the exposure opt-in when switching companies. exposeTokenAsHostSecret now resets in a useEffect keyed on hostContext.companyId, in addition to the existing resets on save and cancel.
  • src/ui/host-secrets.ts — inactive or archived secrets may be rotated. exposeGitHubTokenToPaperclipHost now reads the secret's status and refuses with an actionable message when it is not active, instead of issuing a rotate that the host answers with 422 and that the git-credential probe would skip anyway. Covered by a new test.

pnpm typecheck, pnpm test (351 pass) and pnpm build are green.

@alvarosanchez alvarosanchez reopened this Sep 14, 2026
@alvarosanchez
alvarosanchez merged commit 4be624c into main Sep 14, 2026
2 checks passed
@alvarosanchez
alvarosanchez deleted the feat/paperclip-2026-831-adoption branch September 14, 2026 16:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants