Ship a cross-repository change
Most changes worth making cross a repository boundary: an API and the services that call it, an event and the workers that consume it, a table and the jobs that read it. An agent working in one checkout cannot see those consumers, and a clean local "find usages" says nothing about them. This page covers the whole activity: scoping, finding consumers, agreeing a compatibility strategy, planning per repository, executing, and checking the running services after release.
When you'd do this
- You are changing an API response, request body or route that other services call over HTTP.
- You are changing the payload of an event or message that workers in other repositories consume.
- You are changing a table or collection that services other than yours read or write.
- A feature needs coordinated changes in a producer and one or more consumers, for example an orders API, a billing worker and a mobile backend.
- You are retiring an old field, endpoint or queue and need to know who still depends on it.
If the work stays inside one service, follow Build a feature instead. If the change is a rename or dependency upgrade with no behaviour change, Refactor or upgrade safely covers the mechanics. This page focuses on what is specific to crossing repositories.
Before you start
- CodeZero installed in VS Code with Claude Code, Codex, GitHub Copilot or Cursor as the agent, signed in to your organisation.
- Every repository that might consume the change connected and indexed, not only the one you work in. Code that is not indexed cannot be found.
- Optional but useful: the area you are changing named in Functions, so the scope starts from code that already implements it.
- For the runtime checks: Vörr connected to Claude, ChatGPT or another MCP assistant, with your repositories, cloud accounts, observability tools and CI/CD sources connected. Vörr shows each repository's workspace and branch-to-environment mapping; telemetry and deploys are matched to services by name, with a match label. ChatGPT needs a paid plan with Developer mode turned on.
- Write access to each repository you will change. Approval is a person's job: the agent cannot approve its own plan.
1. Scope the change from the feature map
- In CodeZero, open Functions and find the area you are changing. It shows the entry points, what the feature goes through, the tables and collections it reads and writes, and the other repositories involved.
- Note every repository that appears under other repositories, and every shared table or collection. These are the first candidates for consumers.
- Ask Chat to summarise the current behaviour and the contract you intend to change, with file references.
- Check the feature map against the code: open the entry point and the main handler from the references Chat gives you.
Using the Order checkout feature map, list the entry points, the repositories involved and the tables it reads and writes. Then show me where the order response shape is defined, with file references.Describe the current contract of GET /orders/:id: the response fields, their types and where each is set. I want to change the total field.A good result names the repositories and data stores with file references, not prose alone. If a repository you expect is missing, check that it is indexed before you conclude it is not involved.
2. Find every consumer, including repositories you have not cloned
Consumers come in several forms, and each needs its own search. CodeZero searches the organisation index, so repositories that are not checked out on your machine are included.
- Ask Chat what the change affects. CodeZero answers from the index, including references, callers and inferred cross-repository edges.
- Open Graph on the repository you are changing and look at its incoming edges: HTTP route matches, queue consumers and config references. Each edge shows file:line evidence, and HTTP and config edges also show a confidence.
- Run a text search across the organisation for the field or route name as well. It catches consumers that use the value in ways no edge describes, for example a field name in a mapping file.
- Collect the list of consumer repositories, the file and line in each, and the confidence of each HTTP and config edge.
- Code references
- Imports and uses of a shared type, client or package, found by name across indexed repositories. Same-named symbols can appear, so read each result.
- Callers
- Functions that call the code you are changing, and what they call in turn. These are heuristic, not compiler-accurate.
- HTTP consumers
- Calls in other repositories matched to the routes your repository declares. In source, these are URL strings, not references, so a normal reference search does not find them.
- Queue consumers
- Producers and consumers of the same queue or topic, matched across repositories.
- Config references
- Services that point at yours through Kubernetes, Helm or Compose configuration, such as an ORDERS_API_URL setting.
Before we change anything: which services in any repository read the total field from the orders API, either over HTTP, from the order.created event or from the orders table? Give file and line for each and say which are inferred.List every call in other repositories that matches a route declared in orders-api, grouped by route, with the confidence of each match.Which services reference orders-api through deployment config, and in which repositories is that setting defined?A good result is a list of consumers by repository, each with evidence on both sides of the edge. Treat an empty result with care: it can mean there are no consumers, or that the consumer lives in a repository that is not indexed or builds its URL at runtime.
3. Verify the inferred edges that matter
- For each inferred HTTP edge your plan depends on, use Verify in Graph. For config and queue edges, open the file and line the edge cites. The agent reads the call site and records a verdict: confirmed, wrong, or retargeted to the repository it really reaches.
- Start with low-confidence edges. Low confidence usually means the path matched routes in several repositories, so the target is a guess.
- Open the call site yourself for any consumer the plan will change. Confirm which field it reads and how it handles a missing value.
Verify the edge from billing-worker to GET /orders/:id in orders-api. Read the call site and tell me which response fields it reads and what happens if total is missing.Which of the inferred edges into orders-api are low confidence? Check each call site and tell me where it really goes.Verdicts are kept for the organisation, so the next person who asks sees a confirmed edge instead of a guess.
4. Check the data contract
- Ask CodeZero which code reads and writes each table or collection the change touches. The data layer of Graph shows this across repositories.
- Separate the API contract from the storage contract. Renaming a response field does not require renaming a column, and renaming a column affects every repository that reads it directly.
- List the jobs, reports and workers that read the data directly. They will not see your API change, but they will see a migration.
Which repositories read or write the orders table, and which columns does each use? Mark direct database access as distinct from access through the orders API.A good result lists readers and writers per table with file references. If you plan a migration, each direct reader becomes a consumer in the plan.
5. Decide the compatibility strategy
How the old and new contract coexist is a product and operational decision, not something the code can answer. When you describe the change, CodeZero asks you these questions rather than letting the agent pick an assumption. Your answers are recorded with the plan.
- Expand and contract
- Add the new field or column alongside the old one, move every consumer, then remove the old one in a later release. This is the safest option when consumers deploy independently.
- Keep the old field
- Serve both names indefinitely, or until a deadline. This is simple, but it leaves duplication that someone has to remove.
- Versioned route
- Publish /v2 with the new contract and keep /v1 unchanged. This suits external consumers or large differences, at the cost of maintaining two routes.
- Coordinated switch
- Change the producer and all consumers together. This is only reasonable when every consumer is known, verified and released in the same window.
/plan Rename total to totalAmount in the orders API response, the order.created event and every consumer. Do not rename the database column. Ask me how old and new should coexist before planning.Expect questions such as: should total keep being served, and for how long? Should the event carry both fields? Is the mobile backend released on a different schedule? If you are unsure, say so; the plan can include a follow-up to decide later.
6. Plan across repositories
- Use Planned mode, or /plan in Auto, so it makes no code changes until a plan is approved.
- Check that the plan names every repository it will change. An edit in a repository the plan does not name asks you first, so a missing one is a gap in the plan.
- Check the phase order. Expand-and-contract normally runs producer first (serve both fields), then consumers (read the new field), then a later phase or follow-up to remove the old field.
- Check the verification for each repository: the test, lint or build command that proves that repository's part works. After approval, only these commands run without asking.
- Approve the plan, or ask for changes. The agent cannot approve its own plan.
Before I approve: list the repositories this plan changes, the order of phases and why, and the verification command for each repository. Is there any consumer from the impact check that the plan does not cover?A tech lead's review happens here, on the plan and the impact, rather than later on one large diff with decisions already made inside it.
7. Clone the repositories the plan needs
- Tasks shows any repository in the plan that has no local checkout and offers Clone. Choose where the checkout goes.
- Reading works from the index without a checkout. Writing needs a real working tree, so clone before the tasks for that repository run.
- Create or switch to the right branch in each checkout. Git Tree shows the branches and working tree for each repository.
8. Execute per repository
- Tasks breaks the plan into phases and tasks, each attributed to a repository, with dependencies between them. Consumer tasks wait for the producer task they depend on.
- Run tasks one at a time, or use Run all. Review each task's changes in its own repository before moving on.
- Run each repository's declared verification. A green producer suite says nothing about consumers; each repository's own tests must pass.
- Tasks also lists the work teammates have started, with who started each one, so two people can take different repositories as separate pieces of work without colliding.
- Commit and push from Git Tree in each repository, and open one pull request per repository. Link them to each other so reviewers see the whole change.
For the billing-worker task: show me every place it now reads totalAmount, and confirm it still falls back to total if the field is missing.Once the plan is approved and every question is answered, Coffee Mode can finish the remaining tasks unattended and run their verification. It never commits, pushes or switches branches, so the release order stays with you.
9. Check the running services with Vörr
- Before release, ask Vörr which services and environments sit behind each repository in the plan. It shows each repository's workspace and branch-to-environment mapping, matches telemetry and deploys to services by name, and gives a match label for each result.
- Ask what depends on each affected service at runtime: callers in the service graph and the cloud resources it uses. Dependencies seen at runtime can reveal a consumer the code index did not find.
- After each release, ask Vörr for the deploy feed for those services across your CI/CD sources, then the errors and error rate since the deploy, compared with a baseline window.
- If a consumer starts failing, ask for its top errors with stack and culprit, and the logs around the first occurrence.
For orders-api, billing-worker and mobile-backend: which services and environments does each map to, and what calls each of them in production? Say where a match is by name, suggested or unmapped.Show deploys in the last 24 hours for orders-api, billing-worker and mobile-backend, then compare their error rate and top errors since each deploy with the same window 7 days ago.A good answer gives each fact with its source and environment. If Vörr reports no connector or an unmapped service, you have a gap in coverage, not a clean result.
10. Roll out and follow up
- Release in plan order: producer first, consumers after, each checked with Vörr before the next.
- Record the contract phase as a follow-up: removing the old field, event property or route once every consumer reads the new one.
- Before the removal, repeat the consumer search. New code may have started reading the old field since the first check.
- Run the removal as its own small plan, with the same repositories and verification.
Does any repository still read total from the orders API response or the order.created event? Include repositories changed since last week.Without CodeZero or Vörr
Claude Code can do cross-repository work on its own. If you clone every repository involved and open them together, it can search all of them with ripgrep, edit each one, run each test suite and open pull requests with gh. With vendor MCP servers or CLIs configured, it can also query your error tracker and CI. The costs are in setup and in what it cannot know.
| Operation | Claude Code / ChatGPT on their own | With CodeZero and Vörr |
|---|---|---|
| Know which repositories to look in | You decide which to clone. A consumer in a repository you did not clone is invisible, and an empty search looks the same as a clean one. | CodeZero searches every indexed repository, cloned or not. |
| Find HTTP consumers | Grep for the path or field name in each clone. Works if the URL appears literally; misses base URLs from config and paths built from fragments. | CodeZero matches calls to declared routes across repositories, with confidence and file:line on both sides. |
| Queue and config consumers | Search for topic names and variable names repository by repository, then match them up yourself. | Inferred producer/consumer and config edges in Graph. |
| Data contract | Grep for table and column names; ORMs and query builders make this unreliable. | Data layer shows which code reads and writes each table or collection. |
| Confirm an inferred link | Read the call site; the conclusion lives in that session. | Verify records a verdict the whole organisation sees. |
| Compatibility decision | The agent often picks one silently unless you ask. | CodeZero asks you, and records the answer with the plan. |
| Plan, approval and scope | Plan mode and permission prompts exist, but approval and scope are per-session settings; nothing ties edits to the repositories a plan named. | One plan naming every repository, approved by a person, with confirmation before any edit outside the plan's repositories and per-repo tasks. |
| Runtime check | Possible with a Sentry, Datadog or CI MCP server or CLI per tool, each credentialed on your machine, with service names matched by hand. | Vörr answers across connected tools with resolved service names, source and match quality, plus an organisation-wide deploy feed. |
ChatGPT or Claude chat can reason well over diffs and logs you paste, and over whatever its connectors reach, but it cannot search repositories it has no connector for.
With CodeZero and Vörr
- Consumers beyond the checkout (CodeZero)
- References, callers, HTTP route matches, queue consumers and config references across every indexed repository.
- Evidence and verdicts (CodeZero)
- Each edge shows file:line evidence, and HTTP and config edges also show a confidence. Verify keeps the verdict for the team.
- Data impact (CodeZero)
- Which repositories read and write each table or collection.
- Decisions by people (CodeZero)
- Compatibility questions are asked and recorded, not assumed.
- One plan, many repositories (CodeZero)
- Repositories, ordered phases, per-repo verification, approval, Clone for missing checkouts, confirmation before any edit outside the plan's repositories, and tasks with per-repo attribution and teammates' work.
- Runtime (Vörr)
- Services and environments behind each repository, runtime callers, deploys across CI/CD sources, and errors and metrics per source after release.
- Together
- CodeZero finds the consumers in code; Vörr confirms which of those services are live, where they run and whether they are healthy after each step of the rollout. They are used side by side: CodeZero in VS Code, Vörr from your assistant.
Worked example: rename total to totalAmount
The task: the orders API response field total becomes totalAmount, to make room for a separate taxAmount. Without context, an agent renames the field everywhere in orders-api, updates its tests, and reports that all usages are updated. The pull request is small and green. After deploy, the billing worker reads an undefined total and writes zero-value invoices. The consumer was a URL string in another repository, and nothing in orders-api said who consumes its API.
With CodeZero, the impact question comes first.
Consumers of orders.total
HTTP GET /orders/:id (declared in orders-api src/orders/orders.controller.ts:42)
billing-worker src/invoices/fetchOrder.ts:18 high reads total
mobile-backend app/services/orders_client.rb:31 medium reads total
admin-web src/api/orders.ts:9 low path also matches catalog-api
Queue order.created (produced in orders-api src/events/publish.ts:27)
notifications src/handlers/orderCreated.ts:12 high reads total
Data orders.total (column)
orders-api writes
reporting-jobs reads jobs/daily_revenue.py:54 (direct SQL)
Not cloned locally: billing-worker, mobile-backend, notifications, reporting-jobsVerify on the admin-web edge returns wrong: the call goes to catalog-api. The mobile-backend edge is confirmed; the client reads total and defaults to 0 if it is missing, which would show free orders in the app. reporting-jobs reads the column directly, so the column keeps its name and reporting-jobs stays out of the plan.
CodeZero asks two questions. Should orders-api serve both fields during the transition? Answer: yes, for two releases. Should order.created carry both? Answer: yes. The approved plan:
Plan: totalAmount with expand and contract
Repositories: orders-api, billing-worker, mobile-backend, notifications
Phase 1 Producer (orders-api)
Serve totalAmount and total; publish both in order.created
Verify: npm test && npm run lint
Phase 2 Consumers (depend on phase 1)
billing-worker read totalAmount, fall back to total Verify: npm test
mobile-backend read totalAmount, fall back to total Verify: bundle exec rspec
notifications read totalAmount, fall back to total Verify: npm test
Follow-up (separate plan)
Remove total from response and event after both releasesTasks offers Clone for the three consumer repositories. A teammate starts the mobile-backend part as their own work, and Tasks shows it under their name. Each repository gets its own pull request, cross-linked.
After the orders-api deploy, Vörr shows the deploy in the feed, no new errors on orders-api, and an unchanged error rate for billing-worker against 7 days ago. After the consumer deploys, the same check is repeated per service. Two releases later, the consumer search for total in the response returns only the fallback lines, and the removal runs as its own small plan.
Vörr, after the billing-worker deploy (production)
Deploy billing-worker #482 GitHub Actions 14:05
Errors Sentry billing-worker no new issues since 14:05
Error rate Datadog billing-worker 0.4% (vs 0.4%, 7 days ago)
Match billing-worker explicit (service map)Verify and follow up
- Provenance: every consumer in the impact list should have a file and line you can open, and every Vörr fact a source and environment.
- Match quality: low-confidence edges are questions until verified. Inferred or unmapped service matches in Vörr deserve a second look.
- Coverage: check that every repository you expect is indexed and that the observability and CI tools for each service are connected. An absent result from an unconnected source means nothing.
- Tests: each repository's declared verification passes in that repository, not only the producer's.
- Plan: compare the repositories in the approved plan with the consumers from the impact check. Every consumer is either in the plan or deliberately excluded, with the reason recorded.
- Follow-ups: schedule the contract phase, and re-run the consumer search before removing anything.
Limits
- CodeZero covers indexed, committed code on the default branch. Consumers outside the organisation, in unconnected repositories or in unmerged branches elsewhere, are not visible. A local checkout adds your own branch and uncommitted work.
- Inferred edges come from matching paths, names and config. A URL built from fragments at runtime, a gateway rewrite or a dynamically named queue can be missed or matched with low confidence.
- References are matched by name, and callers are heuristic; neither is compiler-accurate.
- The data layer shows which code reads and writes a table, not every query's columns in every ORM. Check direct readers by hand before a migration.
- Vörr is read-only. It reports deploys, errors and dependencies; it does not deploy, roll back or change infrastructure.
- Runtime checks depend on name matching. A service Vörr cannot match gives an unmapped result rather than a guess.
- Neither product decides the compatibility strategy or the release order. People do, and CodeZero records it.
Related
- Build a feature
- Refactor or upgrade safely
- Review a pull request
- Delegate approved work
- Investigate deployments and CI failures
- Understand system architecture
- Work across repositories
- Explore the Code Graph
- Feature mapping
- Choose an execution mode
- Coffee Mode
- Git Tree
- Ask Vörr about your system
- Connect observability