Scope is a platform for benchmarking AI coding agents. It orchestrates multiple coding agents (GitHub Copilot, Claude Code, VS Code Web), sends them standardized tasks through configurable scenarios and personas, evaluates results using a criteria DAG with the Judge service, and tracks everything with real-time logging.
For the full system architecture, see docs/architecture/overview.md.
pnpm workspaces monorepo. TypeScript, strict mode, ES2022, NodeNext modules.
apps/
api/ # REST API — run orchestration, SSE log streaming, criteria CRUD
cli/ # CLI — submit runs, stream logs, manage criteria
judge/ # Evaluation engine — scores agent output against criteria DAG
portal/ # Web UI — run management, insights, criteria graph editor
token-manager/ # Token storage, validation, round-robin distribution
workers/
coder-acp-copilot/ # GitHub Copilot worker (ACP SDK)
coder-acp-claude-code/ # Claude Code worker (ACP SDK)
report-generator/ # Post-run report generation
model-scanners/ # Feature detection for Copilot and Anthropic models
version-checkers/ # Poll for new agent/tool releases
key-updaters/ # GitHub auth cookie management for VS Code Web
packages/
shared/ # Monorepo foundation — types, DB models, queue/blob/redis clients
db-migrations/ # MongoDB migration framework (mongo-migrate-ts)
github-auth/ # GitHub OAuth/device-code auth utilities
model-scanning/ # Shared model scanning logic
version-checking/ # Version comparison utilities
llm-eval/ # LLM-graded eval harness (grader, sampling, rate-limit retry)
test-utils/ # ACP worker integration test helpers (Docker runner, in-container harness)
config/ # Benchmark definitions (YAML)
For package architecture and data models, see docs/architecture/app-design.md.
The API owns domain validation, orchestration, fan-out, defaulting, and resolution rules shared by Portal, CLI, and direct API consumers. Portal and CLI are thin clients: they collect user intent and may provide early validation, but must not duplicate or materialize API-owned business logic. When one user action expands into multiple domain objects, each object is derived independently by the API from the shared intent so client or sibling state cannot leak between them.
Express.js REST server. Orchestrates runs, streams logs via SSE, manages criteria CRUD, routes tasks to workers through Azure Storage Queues. Connects to MongoDB (CosmosDB-compatible), Redis, Azure Storage Queues, and Blob Storage.
Project-scoping invariant: never perform a global, slug-only get/edit/soft-delete on a project-scoped entity. Every such query must filter by
_idorprojectId— a human slug/business id is never a key on its own. DeriveprojectIdfrom context (a parent doc or the run-request) when available, else require it from the client (?projectId=), else fail 400. See the by-id invariant in docs/architecture/app-design.md.
- Data models and API design: docs/architecture/app-design.md
- SSE + Change Streams pattern: docs/research/realtime-data-flow.md
- Environment variables: ENV_VARIABLES.md
Authentication invariant: verify the unchanged IdP bearer before any user-access cache lookup. Only
POST /api/v1/users/mecallsUserAccessResolver.enrollOnLogin()for JIT/profile/lastLogin/bootstrap writes. EveryGET /users/meand other routes useresolveExisting()(Redis hit: no Mongo; miss/outage: exact identity read, never upsert). Missing/disabled identities deny access, never become anonymous. Preserve existing no-token/public rollout; full RBAC and Scope internal tokens remain deferred. See auth-rbac.md before changing this boundary.
Each worker implements the same queue-processor interface but adapts a different coding agent:
| Worker | Agent | Protocol |
|---|---|---|
coder-acp-copilot |
GitHub Copilot | ACP SDK v0.14.1 |
coder-acp-claude-code |
Claude Code | ACP SDK v0.13.1 |
report-generator |
— | Copilot SDK |
Workers consume tasks from Azure Storage Queues (named queue-<worker-name>) and write results to MongoDB and Blob Storage. Each has its own Dockerfile and docker-compose profile.
- VS Code Web worker architecture: docs/architecture/vscode-web-worker.md
- Skills integration: docs/architecture/skills.md
Evaluation engine that scores agent output against a criteria DAG (directed acyclic graph). Uses the GitHub Copilot SDK for LLM-based evaluation. Two strategies: bundled (all criteria in one session) or independent (topological order, skips descendants of failures).
- CriteriaProvider abstraction: docs/architecture/criteria-provider.md
- Judge configuration variables: ENV_VARIABLES.md
React 19 web UI with Vite, Tailwind CSS, Radix UI (shadcn/ui), TanStack Query, and XYFlow for criteria DAG visualization. Communicates with the API via REST and SSE.
AuthProvider owns the Scope-user handshake: callback → POST /users/me;
cached-account reload → plain /users/me. Gate all eager queries (including
providers outside RequireAuth) until ready; do not treat MSAL account claims as
the Scope UUID/role. Deduplicate account/login work and cancel it on account change
or logout. The enrollment POST is no-store and must never be prefetched/polled.
- Real-time data flow: docs/research/realtime-data-flow.md
Storybook: When adding or modifying portal components, update the corresponding Storybook stories. Use the
storybookskill for guidance.
Use these patterns when extending list/detail or run-submission UX:
-
Keep interaction model consistent across entities.
- Use the same layout contract: central list/table, right filter rail, contextual left detail/settings panel.
- Reuse shared list primitives (
ListLayout,FilterRail,DataTable,CustomizeColumnsPanel,Pagination) instead of ad-hoc page layouts.
-
Prefer in-place creation over navigation breaks.
- For
/runs/new, create profiles in a dialog and keep users on the page. - Reuse shared forms (e.g.
ProfileCreateForm) between full-page and modal flows to avoid behavior drift.
- For
-
Treat action counts as source-of-truth UX.
- Any submit/CTA label must reflect the real backend effect (e.g. expanded run count, not just occurrence count).
- If composition/expansion is shown elsewhere on the page, the primary action label must match it exactly.
-
Composition UIs should make hierarchy explicit.
- Show base profile and variations with distinct visual semantics (badges/labels such as
Base,Var N). - Prefer profile names over IDs in group headers and list cells; use IDs only as fallback.
- When grouped by profile, carry base/variation badges into group headers for scanability.
- Show base profile and variations with distinct visual semantics (badges/labels such as
-
Long forms in dialogs require stable action affordances.
- Use a constrained scroll container with a sticky footer for primary actions.
- Ensure dialog structure uses a non-growing shell (
grid-rows-[auto_minmax(0,1fr)]+min-h-0/overflow-y-auto) so content scrolls without losing actions.
-
Inline control rows should align visually and behaviorally.
- Keep
New…actions on the same row as their picker/search control where possible. - Match control heights and interaction patterns between similar pickers (task/profile/criteria) to reduce cognitive load.
- Keep
Command-line interface built with Commander.js and Ink (React for terminals). Used for submitting runs, streaming logs, managing criteria, and CI/CD automation. Run pnpm cli --help to discover subcommands.
CLI ↔ Portal parity: Every feature available in the Portal must also be available in the CLI. The CLI is the primary interface for CI/CD and power users — it must never lag behind the Portal in capabilities.
- Distribution and standalone installation: docs/architecture/cli-distribution.md
Express service for centralized token storage, validation, and round-robin distribution. Integrates with Azure Key Vault (Lowkey Vault locally). Uses packages/github-auth/ for GitHub OAuth/device-code auth.
- Architecture: docs/architecture/token-manager.md
Monorepo foundation. Exports types (runs, iterations, criteria, scenarios, personas), Mongoose models, and service clients (queue, blob, redis, config loader, criteria-store, criteria-provider). All apps and workers depend on it — breaking changes here affect everything.
- Package dependency graph: docs/architecture/app-design.md
- CriteriaProvider abstraction: docs/architecture/criteria-provider.md
Personas, scenarios (tasks), criteria, and prompt features are stored in MongoDB (source of truth). They can be exported/imported as YAML for portability and version control. The config/ folder contains YAML examples of these data types.
traits.yaml— Evaluation trait dimensions (personality, experience, verbosity, type)personas/— Reviewer personas combining traits (e.g.demanding-senior.yaml,vibe-coder.yaml)scenarios/— Task definitions agents must implement (e.g.hello-world-express.yaml,react-snake-game-v2.yaml)criteria/— Evaluation criteria forming a DAG with parent-child dependencies, consumed by the judgeprompt-features/— Feature flags tracking what capabilities agents request
Built on mongo-migrate-ts. Migrations are TypeScript files with up() and down() methods. MongoDB is CosmosDB-compatible — avoid features not supported by CosmosDB's MongoDB API.
- Run:
pnpm migrate:up,pnpm migrate:down,pnpm migrate:status - Framework docs: docs/architecture/db-migrations.md
- Write for humans: descriptive names, small focused functions, simple control flow, minimal abstraction.
- Separate logical steps with blank lines; avoid dense one-liners and unrelated statements packed together.
- Give every non-trivial function and logical code block a short comment explaining what it does; add intent, assumptions, invariants, or surprising reasons where useful.
- Apply the same rule to tests: comment non-trivial fixture setup, user-flow phases, and invariant assertions. A descriptive test name does not replace intent comments inside a multi-step test; avoid generic
Arrange/Act/Assertlabels and comments that merely repeat the next line. - Document function parameters, types, and their fields/properties with concise JSDoc/TSDoc explaining meaning, constraints, units, optionality, and defaults where relevant.
- Do not narrate trivial or self-evident code. Document public contracts and failure behavior when names and types are insufficient.
- Follow local conventions and formatters; review readability as well as correctness.
pnpm docker:up:infra # Start backing services (MongoDB, Redis, Azurite, Lowkey Vault)
pnpm docker:dev:copilot # Full stack with Copilot worker + portal (hot reload)
pnpm docker:dev:all # All workers + portal + report generator (hot reload)
pnpm dev:api # API only (native)
pnpm dev:portal # Portal only (native)
pnpm dev:<worker-name> # Individual worker (native)
pnpm open:portal # Open portal in browserBefore a large clean Docker or Podman rebuild, inspect the container runtime
storage and any runtime VM filesystem as described in
CONTRIBUTING.md,
then report the available space and Scope-specific resources. Never
automatically run broad prune commands or delete unrelated images, containers,
build cache, or volumes. Any cleanup must target verified Scope resources and
requires explicit user approval; warn that named volumes may contain local
database or authentication data.
For testing against real Azure CosmosDB (e.g. index behavior), a shared dev instance can be provisioned. Each worktree gets its own isolated database. See docs/shared-dev-infra.md for setup and usage.
When making changes to any Rust component (e.g. the AI gateway in apps/gateway/), follow the rust-best-practices skill. This skill is available at .agents/skills/rust-best-practices/SKILL.md and covers idiomatic Rust, ownership patterns, error handling with Result, and performance guidelines.
Co-locate tests next to source as <filename>.test.ts. Framework: Vitest.
pnpm test # Unit tests
pnpm test:coverage # With coverage report
pnpm test:integration # Integration tests (requires .env + Docker)Portal Storybook stories run under Vitest:
apps/portal/src/components/ui/stories.play.test.tsxcomposes theui/*stories and executes theirplay(interaction) functions inside the regular Vitest suite (no@storybook/addon-vitestrequired). It binds a Testing Librarycanvasto the rendered container, so storyplayfunctions must keep depending only oncanvasplus values imported directly fromstorybook/test(userEvent,screen,expect). When you add a newui/*story with aplayfunction, register its module in that harness so it's covered.
Read docs/architecture/prompt-evaluations.md before changing any AI-facing instruction surface.
- Whenever hardcoded system/user prompt text, prompt-building logic, output instructions, or AI-facing tool descriptions are added or modified, update the corresponding production target adapter, curated cases, deterministic checks, composition contract, and/or rubric.
- Register every new runtime static prompt family in the documented inventory, committed evaluation manifest, and JSONL adapter registry.
- Run the smallest relevant explicit prompt-evaluation command before treating
a prompt change as complete. These suites are developer-invoked and are not
part of normal
pnpm testor CI. - A change to user-authored/configurable prompt content does not by itself create a static prompt family.
- Whenever a user-controlled text field that reaches an AI is added or its insertion point, trusted wrapper, role, tools, or security boundary changes, preserve its benign composition contract. Cloud red-team profiles and execution are maintained in a separate follow-up contribution.
This repository is commonly worked on from a fork. When opening a pull request, always target the upstream repository when one is available — do not open the PR against the fork unless the user explicitly asks you to.
- Detect the upstream: check
git remote -v. If anupstreamremote exists (e.g.microsoft/scope), that is the PR base. Theoriginremote is typically your personal fork (e.g.cedricvidal/scope). - Push the branch to your fork (
origin), then open the PR across forks with the upstream as the base:gh pr create \ --repo <upstream-owner>/<repo> \ --base main \ --head <fork-owner>:<branch> \ --title "..." --body-file <path>
- Only fall back to opening the PR against the fork (
origin) when there is noupstreamremote, or when the user explicitly requests it. - If the upstream org enforces SAML SSO and the PR call fails with a
403("Resource protected by organization SAML enforcement"), stop and ask the user to authorize their token for that org via the SSO link, then retry — do not silently downgrade to a fork PR.
Before starting any task, read the docs relevant to the components you will be working on (see the table below). Understanding the existing design, data models, and patterns prevents regressions and duplicated work.
Before completing any task, update the relevant docs to reflect your changes. This is the last step before calling the work done. If you added a new component, added or changed an API, modified data models, introduced a new pattern, or changed deployment behavior, the corresponding doc must be updated (or a new one created and linked here). Documentation is not optional — outdated docs are worse than no docs.
| Document | Description |
|---|---|
| docs/architecture/overview.md | System architecture, component interactions, data flow |
| docs/architecture/app-design.md | Data models, API design, package dependency graph |
| docs/architecture/prompt-evaluations.md | Static prompt quality, datasets, commands, and maintenance rules |
| docs/architecture/data-organization-projects.md | Projects (a single container) to isolate/group data within a cluster; composes with data-tags and auth-rbac |
| docs/architecture/auth-rbac.md | Explicit-login IdP auth, Redis user-access cache, Portal handshake; deferred RBAC/internal-token roadmap |
| docs/architecture/vscode-web-worker.md | XState chat machine, GitHub auth flow, ARIA snapshots |
| docs/architecture/token-manager.md | Token storage, validation, round-robin distribution |
| docs/architecture/criteria-provider.md | CriteriaProvider abstraction, filesystem vs REST backends |
| docs/architecture/skills.md | Agent Skills spec, registration, resolution, delivery |
| docs/architecture/codebases.md | Codebase entity, immutable revisions, source types, worker seeding |
| docs/architecture/db-migrations.md | MongoDB migration framework |
| docs/architecture/cli-distribution.md | CLI bundling, publishing, installation, update check |
| docs/architecture/retry.md | Retry utilities: withRetry function and @Retry decorator |
| docs/architecture/post-processing.md | Post-processing pipeline, ATIF generation, handler extensibility |
| docs/architecture/observability.md | Application telemetry, Azure Monitor OTel distro, custom worker metrics |
| docs/architecture/kubedock.md | Kubedock sidecar, container access for agents, Kustomize Component toggle |
| docs/research/realtime-data-flow.md | SSE + Change Streams, Redis pub/sub, polling patterns |
| docs/research/delta-storage.md | Space-efficient storage of iteration snapshots |
| docs/shared-dev-infra.md | Shared dev infrastructure (CosmosDB) setup and worktree isolation |
| ENV_VARIABLES.md | Environment variable reference |