Skip to content

feat(git): name connections with no handle from the provider - #692

Merged
pikann merged 9 commits into
Paca-AI:masterfrom
gorlix:feat/describe-identity
Nov 3, 2026
Merged

pikann merged 9 commits into
Paca-AI:masterfrom
gorlix:feat/describe-identity

Conversation

@gorlix

@gorlix gorlix commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Summary

When listing connections, core asks the provider who each identity without a handle is and stores the answer. Closes #661.

Why

Identities created by a plugin's data migration have no handle: they show a generic name and @mentions do not match them until someone uses the token.

Changes

  • describe_identity client op, SPI types synced with scripts/sync-git-spi.sh.
  • Listing connections and shared identities describes nameless identities (5 s in total per listing, best effort; the listing never fails because of it).
  • A failure or an unusable answer (another ref, empty handle) pauses that identity for 15 minutes, in memory, so a revoked token or an outage does not slow every listing. A request the client cancels does not pause it.
  • UpdateIdentityProfile never overwrites an existing handle.
  • docs/plugins/git-provider-spi.md and docs/ai-agent/git-agent-workflow.md.

Tests

  • API: all packages pass with -race (about 3274 test results), go vet and golangci-lint clean; every commit builds. Unit tests cover the shared 5 s deadline over several identities, the backoff after unusable answers, no backoff on caller cancel, and that the logged error is masked.
  • End to end on the e2e stack, run on 8a77ef9c (before the shared budget and the log masking, which are covered by unit tests only), with a mock GitLab/GitHub and data seeded in the old plugin tables: names appear after the migration on first listing for GitLab and GitHub; a 401 and a 503 keep the generic name, make one call each and none on later listings; a restart retries and the recovered host gets its name. No token in the logs.
  • TestGitStore_UpdateIdentityProfile (the SQL guard AND handle = '') needs PACA_TEST_PG_DSN and is skipped in CI, so CI does not guard it; run it locally with a Postgres DSN.
  • Not covered end to end: the real 15-minute wait (unit test with a fake clock) and workspace-wide identities.

Dependency and merge order

No Go dependency on the SDK: core keeps its own copy of the wire types. Merge after Paca-AI/plugin-sdk-go#17 so the copy matches a released SDK. Providers that do not declare identity_description never receive describe_identity (the existing describe op is still called as before). Then the GitLab and GitHub plugin PRs (opened once the SDK is tagged).

🤖 Generated with Claude Code

gorlix and others added 8 commits November 2, 2026 10:04
Refs Paca-AI#661

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs Paca-AI#661

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Identities created by a plugin's data migration have an empty handle. When
connections or shared identities are listed, ask providers that declare
identity_description who they are and store the answer.

Refs Paca-AI#661

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs Paca-AI#661

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A revoked token or a platform outage made every listing wait for the call
again. Remember the failure in memory and skip the identity for 15 minutes.

Refs Paca-AI#661

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only update rows whose handle is still empty, so a reconnect that named the
identity meanwhile wins over a lazy describe. Zero rows is a no-op.

Refs Paca-AI#661

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each nameless identity had its own 5 second timeout, so several
unreachable ones could hold a listing for 5 seconds each, one after the
other. Share the timeout across the listing and leave the rest for the
next one, without a backoff since they were never asked. The logged
error comes from the plugin, so mask it like any provider message.

Refs Paca-AI#661

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cancel

An ok answer with another ref or an empty handle now counts as a failure
(logged, identity paused for 15 minutes) instead of clearing the backoff.
A request cancelled by the client no longer pauses the identity; running
out of the shared budget still does. Tests cover a real shared deadline
over several identities and that the logged error is masked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot 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.

✅ No new issues found.

Reviewed changes

  • describe_identity SPI op: Capabilities.IdentityDescription, the DescribeIdentityRequest wire type and Client.DescribeIdentity, with the generated SPI copy, golden contract and docs (docs/plugins/git-provider-spi.md, docs/ai-agent/git-agent-workflow.md) kept in sync.
  • Lazy naming in listings: ListIdentities and ListConnections describe handle-less identities under one shared 5 s budget, best effort, and never fail the listing because of a provider.
  • Persisted profile: new Store.UpdateIdentityProfile writes the handle, and name/expiry when non-empty, only while the stored handle is empty, so a reconnect's handle is never overwritten.
  • Failure handling: a 15-minute in-memory backoff per identity after a provider error or an unusable answer (wrong Ref.ID, empty handle), secrets masked via RedactSecrets, and no backoff when the caller cancels.
  • Tests: unit coverage for the shared budget, backoff, caller-cancel, log masking and unusable answers; a client test and a Postgres-backed store test.

I read the full diff, traced the new code against the git_identities schema and the HTTP DTO, and ran go test, go test -race and go build ./... for the module — all pass. The shared-budget/context handling is sound (budget is a child of ctx, so the caller-cancel check cannot misfire on a budget timeout), s.mu is never held across provider or store calls, and the handle = '' guard matches the schema (no unique or check constraint on handle/display_name).

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@pikann pikann 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.

LGTM! Thanks so much for your contribution.
Could you please resolve the merge conflicts?
BTW, I've released Go SDK v0.4.2, so feel free to update the GitHub plugin for this as well

…ntity

# Conflicts:
#	services/api/internal/service/git/service_test.go
@gorlix

gorlix commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @pikann! Conflicts resolved (merged master, tests and lint green). I'll update the GitLab and GitHub plugins to v0.4.2 next.

@pullfrog pullfrog Bot 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.

✅ No new issues found.

Reviewed changes

The only commit since the prior pullfrog review is a merge of upstream/master (92fe79bb) that resolves the conflicts the maintainer requested; the feature's own code is unchanged.

  • Merge of upstream/master: 92fe79bb integrates master (b0c4dd3, incl. the repository picker and issue listing). The conflict sites — spi_types.go, spi_wiretypes.go, service.go, service_test.go, client_test.go and wire-shapes.golden.json — carry both sides: this PR's IdentityDescription / DescribeIdentityRequest / DescribeIdentity and upstream's issue_list / ListIssuesRequest / IssuePage / ListRepositories.
  • No feature drift: setup.go, describe_test.go, repository.go and git_repository.go are untouched by the merge, so the describe path is byte-identical to what the prior review approved.

I verified the resolution: go build ./... and the full go test ./... for the module pass, the golden contract lists both identity_description and issue_list, and no conflict markers remain. The merge-order dependency on the SDK is now satisfied (v0.4.2 is released).

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@pikann
pikann merged commit 39c95b6 into Paca-AI:master Nov 3, 2026
6 checks passed
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.

SPI: let core refresh a connection's identity (handle empty after plugin migrations)

2 participants