chore: sync fork with upstream (ColeMurray/background-agents)#2
Open
jasoncuriano wants to merge 13 commits into
Open
chore: sync fork with upstream (ColeMurray/background-agents)#2jasoncuriano wants to merge 13 commits into
jasoncuriano wants to merge 13 commits into
Conversation
## Summary - Document Slack's separate Select Menus Options Load URL for external select menus - Point the Options Load URL at the existing `/interactions` endpoint - Clarify that searchable repository pickers require this configuration ## Why Slack quick-pick buttons are rendered inline, but the searchable repo dropdown uses an `external_select`. Slack only requests those dynamic options when the app has a Select Menus Options Load URL configured, so the dropdown can show `no result` even though repos are available. ## Validation - `npx prettier --check docs/GETTING_STARTED.md` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated the Slack setup guide to add a required **Select Menus → Options Load URL** configuration for searchable repository pickers. * Clarified that the Options Load URL should match the same `/interactions` endpoint used for the Request URL. * Renumbered/reformatted the subsequent setup steps to reflect the added configuration. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary - Add an explicit `No repository` option to the homepage session target selector. - Create repo-less sessions from the web composer with `repoOwner: null` and `repoName: null`. - Hide branch selection and omit branch context for repo-less sessions. - Default to `No repository` when the user has no accessible repositories. ## Testing - `npm test -w @open-inspect/web -- src/app/(app)/page.test.tsx` - `npm run typecheck -w @open-inspect/web` - `npm run lint -w @open-inspect/web` - `npm test -w @open-inspect/web` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an explicit “start without a repository” option for session creation. * Updated the repository picker to reflect this choice and only show branch selection when a repository is selected. * **Bug Fixes** * Improved session startup when no repositories are available, including clearer guidance in the empty state. * **Tests** * Added UI flow coverage to ensure sessions can start with a “no repository” target and send the expected session creation payload. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This is an automated nightly unsafe-cast remediation. It replaces selected high-risk TypeScript assertions at OAuth/API trust boundaries with Zod `safeParse` validation, following the TypeScript Coding Standards unsafe-cast / parse-don't-assert guidance and the Zod boundary-validation pattern established in PR ColeMurray#807. | Finding | Risk | Cast removed | Fix | | --- | --- | --- | --- | | `packages/control-plane/src/auth/github.ts:48` | HIGH | `(await response.json()) as GitHubTokenResponse & { error?: string; error_description?: string }` for GitHub OAuth code exchange | Added `githubTokenResponseSchema` as the `GitHubTokenResponse` source of truth and parse through `parseGitHubTokenResponse` with preserved OAuth error handling | | `packages/control-plane/src/auth/github.ts:81` | HIGH | `(await response.json()) as GitHubTokenResponse & { error?: string; error_description?: string }` for GitHub OAuth refresh | Reused `githubTokenResponseSchema.safeParse` at the refresh boundary | | `packages/control-plane/src/auth/openai.ts:50` | HIGH | `response.json() as Promise<OpenAITokenResponse>` for OpenAI OAuth refresh | Replaced the hand-written interface with `openAITokenResponseSchema` / `z.infer` and validate the parsed response before returning | Verification: | Command | Result | | --- | --- | | `npm test -w @open-inspect/control-plane -- src/auth/github.test.ts src/auth/openai.test.ts` | Passed | | `npm run build -w @open-inspect/shared` | Passed | | `npm run build -w @open-inspect/control-plane` | Passed | | `npm run typecheck` | Passed | | `npm run lint` | Passed after temporarily moving the pre-existing untracked `.opencode/` tooling directory out of the workspace so the root lint command evaluated tracked repository files | | `npm run format` | Passed | | `npm test -w @open-inspect/control-plane` | Passed | --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/c0eee7014d656c67ad338b45f014cd85)* Co-authored-by: OpenInspect <open-inspect@noreply.github.com>
This is an automated nightly unsafe-cast remediation. It replaces three high-risk TypeScript assertions at request/internal-session/API trust boundaries with Zod `safeParse` validation, following the TypeScript Coding Standards unsafe-cast / parse-don't-assert guidance and the Zod boundary-validation pattern established in PR ColeMurray#807. | Finding | Risk | Cast removed | Fix | | --- | --- | --- | --- | | `packages/control-plane/src/routes/session-child-spawn.ts:36` | HIGH | `(await request.json()) as SpawnChildSessionRequest` for child session creation input | Added shared `spawnChildSessionRequestSchema` with `z.infer` as the type source and validate the request body with `safeParse`, preserving the existing 400 `title and prompt are required` path | | `packages/control-plane/src/routes/session-child-spawn.ts:82` | HIGH | `(await spawnContextRes.json()) as SpawnContext` for parent session state used to initialize child sessions | Added shared `spawnContextSchema` with nullable fields modeled explicitly and validate the internal response with `safeParse`, preserving the existing 500 `Failed to get parent session context` path | | `packages/github-bot/src/github-auth.ts:132` | HIGH | `(await response.json()) as { permission: string }` for GitHub collaborator permission authorization response | Added a local Zod response schema and validate with `safeParse`, preserving `{ hasPermission: false, error: true }` on malformed API responses | Verification: | Command | Result | | --- | --- | | `npm test -w @open-inspect/shared -- src/types/boundary-schemas.test.ts` | Passed | | `npm test -w @open-inspect/github-bot -- test/github-auth.test.ts` | Passed | | `npm test -w @open-inspect/control-plane -- src/router.spawn-child.test.ts` | Passed after `@open-inspect/shared` rebuild refreshed the new exports | | `npm run build -w @open-inspect/shared` | Passed | | `npm run build -w @open-inspect/control-plane` | Passed | | `npm run build -w @open-inspect/github-bot` | Passed | | `npm test -w @open-inspect/shared` | Passed | | `npm test -w @open-inspect/control-plane` | Passed | | `npm test -w @open-inspect/github-bot` | Passed | | `npm run typecheck` | Passed | | `npm run lint` | Passed after temporarily moving the pre-existing untracked `.opencode/` tooling directory out of the workspace so the root lint command evaluated tracked repository files | | `npm run format` | Passed | | `git diff --check` | Passed | References: TypeScript Coding Standards unsafe-cast / parse-don't-assert guidance; Zod boundary-validation pattern established in PR ColeMurray#807. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/e71a3545b1132e0bd0e5132142bf3fc3)* Co-authored-by: OpenInspect <open-inspect@noreply.github.com> Co-authored-by: Cole Murray <colemurray.cs@gmail.com>
This is an automated nightly unsafe-cast remediation sweep. It replaces selected high-risk response-boundary assertions with Zod validation following the TypeScript Coding Standards unsafe-cast / parse-don't-assert guidance and the Zod boundary-validation pattern established in PR ColeMurray#807. This PR is marked DRAFT in the title/body because two exact verification gates are blocked in this workspace, as detailed below. Requested label: `automation:unsafe-cast` ## Findings Fixed | file:line | risk | cast removed | how it was fixed | | --- | --- | --- | --- | | `packages/web/src/lib/auth.ts:77` | HIGH | `(await response.json()) as GithubEmail[]` from GitHub's `/user/emails` API, feeding auth allowlist email resolution | Package-local Zod schema in `github-email-schema.ts`; validates consumed fields and accepts `visibility: null`; malformed responses fail closed with `[]` | | `packages/github-bot/src/handlers.ts:69` | HIGH | `(await response.json()) as { sessionId: string }`, feeding GitHub bot session state | Package-local Zod schema in `control-plane-responses.ts`; invalid success responses throw through the existing session creation error path | | `packages/github-bot/src/handlers.ts:91` | HIGH | `(await response.json()) as { messageId: string }`, feeding GitHub bot message state | Package-local Zod schema in `control-plane-responses.ts`; invalid success responses throw through the existing prompt delivery error path | ## Verification | command | result | | --- | --- | | `npm run build -w @open-inspect/shared` | PASS | | `npm run build -w @open-inspect/github-bot` | PASS | | `npm run build -w @open-inspect/web` | FAIL: TypeScript passes, then prerender of `/automations/new` fails with `TypeError: Cannot read properties of null (reading 'useContext')`; this page was not touched by this change | | `npm run typecheck` | PASS | | `npm run lint` | FAIL: root lint scans pre-existing untracked `.opencode/` tooling files and reports globals such as `process`, `fetch`, `Headers`; committed files are clean | | `npm run lint -- --ignore-pattern '.opencode/**'` | PASS | | `npm run lint -w @open-inspect/shared` | PASS | | `npm run lint -w @open-inspect/github-bot` | PASS | | `npm run lint -w @open-inspect/web` | PASS | | `npm test -w @open-inspect/shared` | PASS | | `npm test -w @open-inspect/github-bot` | PASS | | `npm test -w @open-inspect/web` | PASS | ## Dependency Check Added `zod@^4.4.3` to `@open-inspect/web` for a genuinely package-local auth boundary. `package-lock.json` changed only to add the corresponding `packages/web` dependency entry; no unrelated lockfile churn remains. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/662960e2a119deb77df0849505476f36)* Co-authored-by: OpenInspect <open-inspect@noreply.github.com> Co-authored-by: Cole Murray <colemurray.cs@gmail.com>
## Summary - remove the exact `total` count from the sessions list API - derive `hasMore` by fetching one extra session row - update archived-session pagination to use the returned `hasMore` flag ## Why The frontend only consumes `sessions` and `hasMore`, so the D1 `COUNT(*)` query was doing unnecessary work on a hot session-list path. Removing it avoids the growing table scan for `status != archived` requests. ## Validation - `npm test -w @open-inspect/control-plane -- session-index` - `npm test -w @open-inspect/web -- session-list control-plane-query data-controls-settings session-sidebar` - `npm run test:integration -w @open-inspect/control-plane -- d1-session-index auth` - `npm run typecheck` - `npm run lint -w @open-inspect/control-plane` - `npm run lint -w @open-inspect/web` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Session lists now use server-provided pagination info, making “Load more” behavior more reliable. * Archived sessions now load more accurately without relying on page size checks. * **Improvements** * Session list responses no longer include a total count and now return a clearer `hasMore` indicator. * Pagination handling was updated across the app to match the new session list response format. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…y#854) ## Problem `scripts/d1-migrate.sh` tracks applied migrations by their **numeric prefix** — `VERSION=$(echo "$FILENAME" | grep -oE '^[0-9]+')`, stored as the `_schema_migrations.version` primary key, and the skip check is `grep -qxF "$VERSION"`. If two migration files ever share a prefix, only one is recorded and the other is **silently skipped — permanently**. A re-run never recovers it, because the number is already marked applied. The result is schema drift that doesn't surface at migrate time; it shows up later as a confusing runtime error (e.g. `table X has no column named Y`). This is easy to hit without anyone doing anything wrong: two PRs in flight each grab the next sequential number (`00NN_...`), both pass CI independently, and after both merge the directory has two `00NN_*.sql` files. One migration is then dropped on the next deploy. ## Fix Detect colliding numeric prefixes at the start of the run and fail with a clear, actionable message instead of silently skipping: ``` ERROR: duplicate migration version prefixes detected: 0022 Renumber the colliding files so each prefix is unique before deploying. ``` No behavior change when prefixes are unique. One-file change, no new dependencies (`sort | uniq -d` over the basenames' prefixes). ## Why a guard rather than reworking the tracking Keying on the full filename instead of the prefix would also avoid the silent skip, but it changes the `_schema_migrations` PK semantics and the INSERT path for existing deployments. The guard is the minimal, behavior-preserving fix; it just turns a silent data-loss case into a loud, self-explaining failure. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Added a pre-flight validation for migration files to stop the migration process when two SQL files share the same version prefix. * Migrations now fail fast with clear error output when duplicate migration numbers are detected (and require filenames with numeric prefixes). <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This is an automated nightly unsafe-cast remediation sweep. It replaces selected scheduler boundary assertions with Zod parsing so untrusted internal request bodies are validated before they drive automation matching, run creation, or run completion state. This follows the TypeScript Coding Standards guidance for unsafe casts / parse-don't-assert and the Zod boundary-validation pattern established in PR ColeMurray#807. | Finding | Risk | Cast removed | Fix | | --- | --- | --- | --- | | `packages/control-plane/src/scheduler/durable-object.ts:427` | HIGH | `(await request.json()) as AutomationEvent` for scheduler automation events feeding matching, dedup, and run creation | Shared `automationEventSchema` Zod discriminated union in `@open-inspect/shared`, parsed with `safeParse` and 400 on invalid payload | | `packages/control-plane/src/scheduler/durable-object.ts:598` | HIGH | `(await request.json()) as { automationId: string }` for manual trigger run creation | Package-local Zod `manualTriggerBodySchema`, parsed with `safeParse` and existing 400 `automationId required` response | | `packages/control-plane/src/scheduler/durable-object.ts:686` | HIGH | `(await request.json()) as { automationId; runId; sessionId; messageId?; success; error? }` for run-complete state transitions | Package-local Zod `runCompleteBodySchema`, parsed with `safeParse` and 400 on invalid callback | Verification commands run in a clean worktree for this branch: | Command | Result | | --- | --- | | `npm run build -w @open-inspect/shared` | Passed | | `npm run build -w @open-inspect/control-plane` | Passed | | `npm run typecheck` | Passed | | `npm run lint` | Passed | | `npm run format` | Passed | | `npm test -w @open-inspect/shared` | Passed, 22 files / 275 tests | | `npm test -w @open-inspect/control-plane` | Passed, 89 files / 1415 tests | Note: in the original workspace, `npm run lint` was also attempted and failed only because of a pre-existing untracked `.opencode/` directory outside this PR; the exact command passed in the clean worktree containing only committed branch files. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/e7ba177d5123895cea9e49c69689c0e1)* --------- Co-authored-by: OpenInspect <open-inspect@noreply.github.com> Co-authored-by: Cole Murray <colemurray.cs@gmail.com>
This is an automated nightly unsafe-cast remediation sweep. It replaces selected high-risk TypeScript boundary assertions with parse-don't-assert validation, following the TypeScript Coding Standards for unsafe casts and the Zod boundary-validation pattern established in PR ColeMurray#807. | Finding | Risk | Cast removed | Fix | | --- | --- | --- | --- | | `packages/web/src/hooks/use-session-socket.ts:139` | HIGH | WebSocket message `raw as WsMessage` after only checking for a `type` key | Added shared `serverMessageSchema` in `@open-inspect/shared` and now `safeParse`s WebSocket messages before handling them. | | `packages/web/src/app/api/sessions/[id]/title/route.ts:27` | HIGH | `await request.json() as { title?: string }` on an API request body | Added a minimal inline parser for the request body that preserves the existing 400 invalid-body path. | ## Verification | Command | Result | | --- | --- | | `npm run build -w @open-inspect/shared` | Passed | | `env -u NODE_ENV npm run build -w @open-inspect/web` | Passed; the plain inherited shell environment had a non-standard `NODE_ENV` that caused Next prerendering to fail before this retry. | | `npm run typecheck` | Passed | | `npm run lint` | Passed after temporarily moving the untracked local `.opencode/` tool directory aside and restoring it; the tracked repo files lint clean. | | `npm run format` | Passed | | `npm test -w @open-inspect/shared` | Passed: 21 files, 274 tests | | `npm test -w @open-inspect/web` | Passed: 48 files, 513 tests | --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/bc3972ace378acf23238764498c05c1f)* Co-authored-by: OpenInspect <open-inspect@noreply.github.com> Co-authored-by: Cole Murray <colemurray.cs@gmail.com>
## Summary Linear agent-session auth failures now preserve the concrete reason they failed instead of collapsing every OAuth problem into a bare missing-client return. When a workspace token is missing, malformed, expired without a refresh token, rejected by Linear during refresh, or blocked by a KV/read failure, the token loader raises a `LinearAuthError` with the failure reason and any available OAuth response details. Agent-session webhooks catch that error, log `agent_session.no_oauth_token` with the org, issue, trace ID, mode, and auth failure reason, then stop before creating or prompting an Open-Inspect session. The final behavior is intentionally log-only for these agent-session paths. Earlier fallback issue-comment handling was removed after review because a failed OAuth refresh usually means operators should inspect the structured logs rather than trying to notify Linear through the static API-key fallback. ## Validation - npm run lint - npm run typecheck - npm test ## Post-Deploy Monitoring & Validation - Watch Linear bot logs for `agent_session.no_oauth_token`, `oauth.refresh_failed`, `oauth.refresh_error`, and `oauth.token_read_error`. - Healthy signal: auth failures include `auth_failure_reason`, issue context, and trace ID, with no control-plane session or prompt created after the auth failure. - Failure signal: repeated `token_read_error` / `refresh_error` spikes, missing issue context in `agent_session.no_oauth_token`, or unexpected Linear agent-session requests continuing after auth failure. - Validation window: first deploy after merge; owner: operator handling the Linear bot deploy. Related: ColeMurray#863 --- [](https://github.com/EveryInc/compound-engineering-plugin)  <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved handling of expired or invalid Linear OAuth tokens, reducing failed agent session starts and follow-ups. * When authentication can’t be restored, the system now skips the action safely and records clearer failure details. * Refresh failures are now classified more consistently, helping diagnose issues like invalid grants and token read errors. * **Tests** * Added coverage for token expiry, refresh success/failure, and agent session flows. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Support a custom domain for the Cloudflare web Worker Adds the ability to serve the web app on a custom domain (e.g. `app.example.com`) instead of the default `*.workers.dev` URL when `web_platform = "cloudflare"`. There was an existing `cloudflare_zone_id` terraform variable, but it was unused. ### Changes - **`variables.tf`** — new optional `cloudflare_custom_domain` variable (the hostname to attach), alongside the existing `cloudflare_zone_id`. Defaults to `null`. - **`web-cloudflare.tf`** — new `cloudflare_workers_custom_domain.web_app` resource that attaches the hostname to the web Worker. Cloudflare provisions the DNS record and edge certificate. Gated on `local.web_custom_domain_enabled` and depends on the Worker deploy. - **`locals.tf`** — added `web_custom_domain_enabled` flag and a third case to `web_app_url`. When a custom domain is set, the app URL (used for `NEXTAUTH_URL` and cross-service config) becomes `https://<custom-domain>` instead of the `workers.dev` URL. - **`terraform.tfvars.example`** — documented the new `cloudflare_custom_domain` input under the Cloudflare zone section. ### Behavior The custom domain only activates when **all** of the following are true: - `web_platform = "cloudflare"` - `cloudflare_zone_id` is set - `cloudflare_custom_domain` is non-empty Otherwise the deployment falls back to the existing `workers.dev` URL — no change for existing deployments. ### Notes - Requires a Cloudflare zone you control (set `cloudflare_zone_id`). - The Cloudflare API token needs **Workers Routes: Edit** to manage the custom domain. - `terraform validate` passes cleanly (dropped the deprecated `environment` attribute on the resource). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added optional Cloudflare custom-domain support for production web workers, including automatic URL switching to the custom hostname when configured. * **Safety Checks** * Added validation to prevent misconfigured custom-domain/zone settings; falls back to the default workers subdomain when required inputs are missing. * **Documentation** * Expanded production Cloudflare setup guidance and examples for the custom-domain option. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…oleMurray#882) Follow-ups to ColeMurray#747 (custom domain support for the Cloudflare web Worker). ## Bug: `terraform plan` fails for every Cloudflare-platform deployment without a custom domain Terraform's `coalesce()` errors when **all** arguments are null or empty strings — `coalesce(null, "")` is a hard error, not `""`: ``` Call to function "coalesce" failed: no non-null, non-empty-string arguments. ``` The `web_custom_domain_enabled` local from ColeMurray#747 evaluates `trimspace(coalesce(var.cloudflare_custom_domain, ""))` whenever `web_platform == "cloudflare"` (`&&` short-circuits, so Vercel deployments escape). Both `cloudflare_custom_domain` and `cloudflare_zone_id` default to `null`, so any existing `web_platform = "cloudflare"` deployment that hasn't opted into a custom domain now fails at plan time. Setting `cloudflare_custom_domain = ""` explicitly errors the same way. This slipped past `terraform validate` because validate treats variables as unknown values — the error only fires on plan/apply with concrete values. **Fix**: null-safe normalization locals (`var.x == null ? "" : trimspace(var.x)`) consumed by the gate, the host, and the `cloudflare_workers_custom_domain` resource. This also fixes the inconsistency where the gate trimmed the domain but the resource/URL consumed the raw value. ## Hard validation instead of an advisory warning The `check` block from ColeMurray#747 had the same `coalesce` problem (it would report an evaluation error rather than its intended message), and as a warning it let a misconfigured domain silently fall back to workers.dev — leaving `NEXTAUTH_URL` and OAuth callbacks pointing somewhere the operator didn't intend. Replaced with two hard failures: - a `validation` block on `cloudflare_custom_domain` for hostname shape: must be a bare hostname — rejects scheme, port, path, trailing dot, whitespace, wildcards - a `terraform_data.cloudflare_custom_domain_gate` precondition in `checks.tf` for the cross-input policy (same pattern as the existing `access_control_gate`), expressed against the normalized locals as `local.web_custom_domain == "" || local.web_custom_domain_enabled`: a set domain requires `web_platform = "cloudflare"` and a non-empty `cloudflare_zone_id` (also catches the previously-silent domain-set-on-Vercel case) `null`, `""`, and whitespace-only all still mean "not configured". ## Single canonical origin Attaching a custom domain doesn't disable the workers.dev route, so the app was reachable on two origins with `NEXTAUTH_URL` pointing at only one. The generated `wrangler.production.toml` now sets `workers_dev = false` when the custom domain is enabled (`true` otherwise — existing behavior, now explicit). > **Note for deployers already running ColeMurray#747 with a custom domain**: after the next apply, the workers.dev URL for the web app stops serving; the custom domain is the single origin. ## Docs - `GETTING_STARTED.md`: custom-domain subsection under Step 8 (vars, token permission, OAuth-callback reminder), custom-domain entries in the callback-URL list and redirect-URI troubleshooting, commented vars in the tfvars sample. - `terraform.tfvars.example`: notes that workers.dev is disabled and GitHub/Google OAuth callback URLs must be updated to the new hostname. ## Verification `terraform fmt -check` and `terraform validate` pass. Validation and locals exercised end-to-end with `terraform plan` on a minimal harness under Terraform 1.14.6 (the repo's required floor): | Scenario | Result | | --- | --- | | all unset (defaults) | plans cleanly, `enabled = false` — previously the coalesce error | | domain set, `web_platform = "vercel"` | hard error (precondition gate) | | domain set, zone id unset | hard error (precondition gate) | | domain + zone, `web_platform = "cloudflare"` | `enabled = true`, host = custom domain | | `https://app.example.com` | hard error (bare-hostname rule) | | domain `""` | plans cleanly, `enabled = false` | <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for serving the web app on a Cloudflare custom domain. * Expanded setup docs with Cloudflare-specific DNS, certificate, and OAuth callback guidance. * **Bug Fixes** * Improved handling of custom-domain settings by normalizing empty values and whitespace. * Tightened validation so custom domains must be valid hostnames and require the related Cloudflare zone setting when used. * Updated production routing to disable the default `workers.dev` route when a custom domain is enabled. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary - Take over ColeMurray#734 and resolve conflicts with current `main`, preserving archived-project filtering. - Use GitLab `namespace.full_path` for repository owner mapping so nested groups round-trip correctly. - Preserve nested group separators in manual MR URLs while still encoding individual path segments. ## Test plan - `npm test -w @open-inspect/control-plane -- gitlab-provider.test.ts` - `npm run typecheck -w @open-inspect/control-plane` --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/8b33d071aa6249421aeed1640d5ca6f7)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Fixed repository ownership and listing for projects inside nested GitLab groups. * Improved manually generated merge request links so nested group paths open correctly. * Preserved archived repository filtering while updating project path handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Mikhail Dubov <mikhail@chattermill.io>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Syncs this fork's
mainwithColeMurray/background-agents:main(13 commits, fast-forward, no conflicts).Includes: custom domain support for web, GitLab full namespace path fix, several type-safety boundary validation fixes, D1 duplicate migration version guard, Linear OAuth failure classification, and a few smaller fixes.