Skip to content

test(evals): cover GitHub connector repo pagination and marketplace skill import end-to-end - #3529

Merged
benjaminshafii merged 2 commits into
devfrom
spec/github-connector-import
Aug 5, 2026
Merged

test(evals): cover GitHub connector repo pagination and marketplace skill import end-to-end#3529
benjaminshafii merged 2 commits into
devfrom
spec/github-connector-import

Conversation

@benjaminshafii

Copy link
Copy Markdown
Member

Follow-up coverage for #3523 (fixes #3521) and #3524 (fixes #3522), per the repo validation standard (evals/specs/** + @openwork/testkit).

What this adds

  • evals/packages/labs/src/mock-github.ts — a deterministic GitHub provider witness (node:http), following the mock-google pattern: installation token minting, /installation/repositories with real GitHub paging semantics (default per_page=30) so the pagination regression stays observable, repo/branch/commit/tree/contents endpoints with deterministic content-hash shas, and a request log capturing method/path/query/ISO-time/authorization for per-identity witness assertions.
  • evals/specs/github-connector-import.slow.test.ts — a stack spec that cold-boots a real Den (server(), ephemeral MySQL DB), points the connector at the witness, and drives the org API end-to-end: create connector account (installation 777, 130 repositories) → list repositories → GitHub setup on a private Claude marketplace repo (branch develop, mirroring the original user report) → discovery → apply → config-object versions → re-apply.
  • ee/apps/den-api/.../github-app.ts — a lazy GITHUB_CONNECTOR_API_BASE env override (4 lines) so specs can aim the connector client at a witness. Read per-request, no env-module import (unit tests import this module bare), production default unchanged.

Claims (all hold in the tape, every one with an observable assertion)

  1. The repository picker returns all 130 installation repositories in provider order across cursor pages, including the private repo with its marketplace manifest badge (regression: only the first 30 before fix(den-api,den-web): list all GitHub installation repositories, not just the first 30 #3524).
  2. Provider enumeration requests exactly pages 1 and 2 at per_page=100 — no unpaged call, no page 3.
  3. All repository/content traffic carries the installation access token, not the app JWT (witness per-request authorization attribution).
  4. Discovery classifies the private marketplace repo, one supported plugin, skills componentPath, zero warnings.
  5. Apply returns 200 and materializes exactly one skill with frontmatter-derived title: probe / description (regression: invalid_skill_frontmatter on every apply before fix(den-api): keep SKILL.md frontmatter when deriving GitHub connector skill projections #3523).
  6. The companion markdown under the skill directory is not imported (negative half: exactly one materialized object, at the SKILL.md path).
  7. The stored version strictly equals the provider SKILL.md after the store's product-wide surrounding-whitespace canonicalization (frontmatter + body bytes intact). The claim names the canonicalization explicitly: normalizeOptionalString trims every config-object version, including manual creates — product behavior, deliberately not changed here.
  8. Re-applying the same revision reuses the config object, creates no duplicate version, and refetches no file content (witness: exactly one SKILL.md contents request across both applies — the blob-sha revision guard).

Commands run

  • pnpm exec vitest run --config vitest.config.ts --project stack specs/github-connector-import.slow.test.ts (from evals/, Docker MySQL on 3306) → 1 passed, ~20s, three consecutive fresh-DB cold-boot runs plus one at this exact HEAD; zero skips.
  • bun test ee/apps/den-api/test/github-connector-app.test.ts → 8 pass (seam did not disturb unit tests).
  • tsc -p . in evals/ → the only error is pre-existing on dev in untouched connectors-quick-add.slow.test.ts (MockMcpHandle.handshakes); the new files are clean.

Evidence tape publishes as the sticky PR comment (ambient testkit tape; screenshots don't apply — API-driven spec, verdict comes from the tape facts).

…kill import end-to-end

Adds a deterministic GitHub provider witness (labs mock-github) and a
stack spec that cold-boots Den, connects a 130-repository installation,
and imports a private Claude marketplace repo through the connector:

- all 130 repositories listed across cursor pages (regression: first
  30 only before #3524), provider paged at per_page=100 pages 1-2,
  installation-token identity asserted per request
- discovery + apply materializes the probe skill with frontmatter-
  derived title/description (regression: invalid_skill_frontmatter on
  every apply before #3523), companion markdown excluded, stored
  source byte-identical after canonical whitespace trim, re-apply
  idempotent with no content refetch

den-api gains a lazy GITHUB_CONNECTOR_API_BASE override so specs can
point the connector at the witness; production default is unchanged.
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openwork-app Ready Ready Preview Aug 5, 2026 4:46am
openwork-den Ready Ready Preview Aug 5, 2026 4:46am
openwork-den-worker-proxy Ready Ready Preview Aug 5, 2026 4:46am
openwork-diagnostics Ready Ready Preview Aug 5, 2026 4:46am
openwork-landing Ready Ready Preview, v0 Aug 5, 2026 4:46am

@benjaminshafii

benjaminshafii commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Photo roll — the-github-connector-lists-every-installation-repository-and-imports-marketplace — 0/0 frames passed · 8 facts

0/0 frames passed · 8 facts · 8 expectations passed · 0 failed

ℹ️ FACT — 1. The repository picker returns all 130 installation repositories across cursor pages

The first page contained 100, the second contained 30, and the concatenated IDs were 1 through 130 in provider order (regression: only the first 30 before #3524).

ℹ️ FACT — 2. GitHub repository enumeration requests provider pages 1 and 2 at 100 items per page

Provider pagination requests were [{"page":"1","perPage":"100"},{"page":"2","perPage":"100"},{"page":"1","perPage":"100"},{"page":"2","perPage":"100"}]; there was no unpaged request or page 3 request.

  • PASS GitHub repository enumeration requests provider pages 1 and 2 at 100 items per page — Provider pagination requests were [{"page":"1","perPage":"100"},{"page":"2","perPage":"100"},{"page":"1","perPage":"100"},{"page":"2","perPage":"100"}]; there was no unpaged request or page 3 request.

ℹ️ FACT — 3. Repository discovery uses the GitHub installation access token

522 repository and content requests carried Bearer mock-installation-token-777, not the app JWT.

  • PASS Repository discovery uses the GitHub installation access token — 522 repository and content requests carried Bearer mock-installation-token-777, not the app JWT.

ℹ️ FACT — 4. Discovery classifies the private marketplace repository with one supported plugin and no warnings

Classification was claude_marketplace_repo; plugin support was true; discovery and plugin warnings were empty.

  • PASS Discovery classifies the private marketplace repository with one supported plugin and no warnings — Classification was claude_marketplace_repo; plugin support was true; discovery and plugin warnings were empty.

ℹ️ FACT — 5. Applying the marketplace import materializes the frontmatter-derived probe skill

Apply returned HTTP 200; the only materialized object was probe-plugin/skills/probe/SKILL.md titled probe (regression: invalid_skill_frontmatter before #3523).

ℹ️ FACT — 6. The companion markdown next to the skill is not imported

The repository tree included references/notes.md, while apply materialized 1 config object at probe-plugin/skills/probe/SKILL.md.

  • PASS The companion markdown next to the skill is not imported — The repository tree included references/notes.md, while apply materialized 1 config object at probe-plugin/skills/probe/SKILL.md.

ℹ️ FACT — 7. The imported SKILL.md keeps frontmatter and body byte-for-byte after canonical whitespace trimming

The stored version strictly equaled the provider source after surrounding-whitespace canonicalization; normalizeOptionalString trims every config-object version, including manual creates.

  • PASS The imported SKILL.md keeps frontmatter and body byte-for-byte after canonical whitespace trimming — The stored version strictly equaled the provider source after surrounding-whitespace canonicalization; normalizeOptionalString trims every config-object version, including manual creates.

ℹ️ FACT — 8. Re-applying the same revision creates no duplicate version and refetches no file content

Both applies returned config object cob_01kz83xkj5enetbf0xh732yxwr; versions remained 1; probe-plugin/skills/probe/SKILL.md was fetched 1 time since 2026-08-05T04:46:44.774Z.

  • PASS Re-applying the same revision creates no duplicate version and refetches no file content — Both applies returned config object cob_01kz83xkj5enetbf0xh732yxwr; versions remained 1; probe-plugin/skills/probe/SKILL.md was fetched 1 time since 2026-08-05T04:46:44.774Z.

Roll created 2026-08-05T04:46:25.773Z · Source: evals/results/rolls/2026-08-05T04-46-25-773Z-the-github-connector-lists-every-installation-repository-and-imports-marketplace/roll.json · Repro: pnpm fraimz:publish -- --pr 3529 --roll 2026-08-05T04-46-25-773Z-the-github-connector-lists-every-installation-repository-and-imports-marketplace

diff-warden[bot]
diff-warden Bot previously approved these changes Aug 4, 2026

@diff-warden diff-warden Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warden security clearance: clear. No new security issues found in this diff (d70ee25d4cd92c90f19bc03b10667106432919b5). Automated clearance satisfies the required-review gate only — a human still reviews and merges. Analysis run

Comment thread evals/packages/labs/src/mock-github.ts Fixed
CodeQL flagged js/stack-trace-exposure on the witness's catch-all
(code scanning alert #202, blocking PR #3529). Log the detail
server-side and return a generic 500 body instead.

@diff-warden diff-warden Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warden security clearance: clear. No new security issues found in this diff (133dc458fb57eca1f4a0595d483786e94242f601). Automated clearance satisfies the required-review gate only — a human still reviews and merges. Analysis run

@benjaminshafii
benjaminshafii merged commit 7661fca into dev Aug 5, 2026
25 checks passed
benjaminshafii added a commit that referenced this pull request Aug 5, 2026
…econciliation, renames (#3536)

* test(evals): cover GitHub connector repo pagination and marketplace skill import end-to-end

Adds a deterministic GitHub provider witness (labs mock-github) and a
stack spec that cold-boots Den, connects a 130-repository installation,
and imports a private Claude marketplace repo through the connector:

- all 130 repositories listed across cursor pages (regression: first
  30 only before #3524), provider paged at per_page=100 pages 1-2,
  installation-token identity asserted per request
- discovery + apply materializes the probe skill with frontmatter-
  derived title/description (regression: invalid_skill_frontmatter on
  every apply before #3523), companion markdown excluded, stored
  source byte-identical after canonical whitespace trim, re-apply
  idempotent with no content refetch

den-api gains a lazy GITHUB_CONNECTOR_API_BASE override so specs can
point the connector at the witness; production default is unchanged.

* feat(den-api): self-healing GitHub connector sync — queue, retries, reconciliation, renames

Webhook deliveries now ack in-request (202) and enqueue connector sync
events; a background worker (workers/github-sync.ts) claims them with
per-target serialization and optimistic row guards, retries transient
provider failures with jittered exponential backoff (attempt_count /
next_attempt_at columns), and dead-letters after max attempts. The
sync-event Retry action re-enqueues into the same worker instead of
flipping a row nothing consumed.

A reconciliation sweep compares each active target's branch head to the
last completed sync and enqueues a manual_resync on drift, healing lost
webhook deliveries; POST /v1/connector-instances/:id/sync-now (and a
den-web Sync now button) enqueues the same check on demand.

repository.renamed webhooks now follow the rename by repositoryId
(previously sync silently died on rename); installation_repositories
removals mark the instance disabled with a recorded event.

Also fixes a pre-existing production bug this surfaced: connector
automation contexts (session: null) tripped the interactive fresh-
privileged-session reauth guard, so webhook-driven auto-import always
failed with a reauth error. Automation contexts now carry an explicit
automation flag that only server-side builders can set.

Spec: evals/specs/github-sync-self-healing.slow.test.ts encodes the
approved six-frame demo against the mock-github witness (now with
fault injection, head advancement, and renames).

Fixes #3530
Fixes #3531
Fixes #3532

* fix(evals): do not echo handler errors into mock-github HTTP responses

CodeQL flagged js/stack-trace-exposure on the witness's catch-all
(code scanning alert #202, blocking PR #3529). Log the detail
server-side and return a generic 500 body instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants