Skip to content

[issue-3393][slice-3/6] Stop the repos[0] collapse... - #3424

Merged
jwbron merged 7 commits into
mainfrom
egg/issue-3393/slice-3
Jul 2, 2026
Merged

[issue-3393][slice-3/6] Stop the repos[0] collapse...#3424
jwbron merged 7 commits into
mainfrom
egg/issue-3393/slice-3

Conversation

@james-in-a-box

@james-in-a-box james-in-a-box Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Remove all three enumerated repos[0] collapse sites and expose the full repo→worktree map to the agent environment, re-keyed by full owner/repo (operator ruling #6). Today the worktree dict keys by bare repo name (_worktree.py:74-79) and would collide on same-name repos under different owners. A ratchet test plus a grep -rn 'repos\[0\]' sweep guard against regressions, explicitly allowing the len(repos)==1-guarded sandbox/egg_lib/sdlc_hitl.py:82 (not a collapse).

Base PR: #3418

What's in this PR

Commits (6):

.egg-state/agent-outputs/coder/brc-memory-issue-3393.md |   64 +
 .egg-state/brc-history/3393-implement-slice-3.json      | 2740 +++++++++++++++++++++++++++++++
 .egg-state/brc-history/3393-implement-slice-3.md        | 3048 +++++++++++++++++++++++++++++++++++
 gateway/gateway.py                                      |   12 +-
 orchestrator/commit_authorship_store.py                 |   27 +-
 orchestrator/gateway_client/_models.py                  |    5 +-
 orchestrator/gateway_client/_worktree.py                |    9 +-
 orchestrator/kubernetes_spawner/_spawn.py               |   41 +-
 orchestrator/routes/pipelines.py                        |   34 +-
 orchestrator/tests/test_commit_authorship_store.py      |   95 ++
 orchestrator/tests/test_gateway_client.py               |   30 +
 orchestrator/tests/test_kubernetes_spawner.py           |  245 +++
 orchestrator/tests/test_overseer_repo_resolution.py     |  110 ++
 13 files changed, 6435 insertions(+), 25 deletions(-)

This slice

Stop the repos[0] collapse; owner/repo-keyed worktree map in agent env

Files affected:

  • orchestrator/kubernetes_spawner/_spawn.py
  • orchestrator/commit_authorship_store.py
  • orchestrator/routes/pipelines.py
  • orchestrator/gateway_client/_worktree.py
  • gateway/gateway.py
  • orchestrator/tests/test_kubernetes_spawner.py
  • orchestrator/tests/test_gateway_client.py
Tasks (3) + acceptance criteria
  • task-3-1: Remove the three collapse sites and thread the full repo set through. (a) orchestrator/kubernetes_spawner/_spawn.py:452 (repo_name = repos[0].split("/")[-1]) and :464 (pipeline_repo = repos[0]) plus the EGG_PIPELINE_REPO export at :523 — replace with logic that derives naming from the pipeline's primary repo but exposes the FULL repo→worktree map to the agent env keyed by full owner/repo (feeding from the client worktrees field → spawner repo_volumes). Keep EGG_PIPELINE_REPO populated with the primary for back-compat but add the full map (e.g. an EGG_PIPELINE_REPOS / repo_volumes env exposure) so agents can select the slice's repo. (b) orchestrator/commit_authorship_store.py:932-933 — replace the repos[0] fallback with primary-repo resolution that does not assume a single repo. (c) orchestrator/routes/pipelines.py:732 (overseer_repo = pipeline_repos[0]) — resolve via the primary accessor. Use the primary_repo accessor from slice 1 rather than re-introducing index-0 access.
    • Acceptance criteria: - All three collapse sites (_spawn.py:452/:464, commit_authorship_store.py:932-933, pipelines.py:732) no longer index repos[0]; they use the primary accessor or the full map. - The agent environment exposes the full repo→worktree map keyed by full owner/repo (no bare-name collision). - EGG_PIPELINE_REPO still resolves to the primary for back-compat.
  • task-3-2: Re-key the worktree map by full owner/repo at its source. In orchestrator/gateway_client/_worktree.py:74-79 the worktrees dict is keyed by bare repo name (repo.split("/")[-1]-shaped); change the key to the full owner/repo so two repos with the same short name under different owners no longer collide, and update the corresponding gateway handler keying in gateway/gateway.py (worktree creation response, ~7827) to match. Preserve the returned path values; only the key changes. Update the spawner consumer (fed via repo_volumes) to read the owner/repo keys.
    • Acceptance criteria: - WorktreeResult.worktrees (and the gateway handler that builds it) is keyed by full owner/repo. - Same-name-different-owner repos get distinct entries. - The spawner reads the owner/repo-keyed map; paths are unchanged.
  • task-3-3: Add tests + a regression ratchet in orchestrator/tests. Assert: the agent env exposes the full owner/repo-keyed repo→path map; each of the three former collapse sites now resolves via the primary accessor / full map (unit-level assertions on the spawner env, commit-authorship resolution, and overseer-repo resolution for a 2-repo pipeline); and a ratchet test that greps the orchestrator source for repos[0] collapse patterns and fails if any reappear, with an explicit allowlist entry for the len(repos)==1-guarded sandbox/egg_lib/sdlc_hitl.py:82. Also assert the worktree map keying is owner/repo (no bare-name collision) via test_kubernetes_spawner / test_gateway_client.
    • Acceptance criteria: - Tests assert the full owner/repo-keyed env map and correct resolution at all three former collapse sites for a multi-repo pipeline. - A ratchet test fails if a repos[0] collapse is reintroduced, with sdlc_hitl.py:82 allowlisted. - make test (narrowed) and make test-all green.

Stack

egg and others added 7 commits July 2, 2026 05:22
task-3-1: remove the three repos[0] collapse sites. _spawn.py derives naming (EGG_REPO_PATH, EGG_PIPELINE_REPO) from the primary via next(iter(...)) and exports the FULL owner/repo->container-path map as EGG_PIPELINE_REPOS (AC-4). commit_authorship_store.py replaces the blind first-alphabetical repos[0] fallback with EGG_PIPELINE_REPO primary-aware resolution (degrades to egg-preferred/first). routes/pipelines.py resolves overseer_repo via next(iter(...)) not pipeline_repos[0].

task-3-2: re-key the worktree map by full owner/repo at its source. gateway/gateway.py worktree_create keys the response by the owner/repo slug (operator ruling #6); on-disk dir/mount stay bare name. gateway_client/_models.py + _worktree.py document the keying. routes/pipelines.py pipeline-level consumer matches pipeline.repo (full slug) and strips the owner prefix when reconstructing the worktree path.

N=1 pipelines behaviorally unchanged. session_create endpoint keying intentionally left bare-name (separate flow, out of slice-3 scope).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ests (task-3-3)

TestMultiRepoAgentEnv: a multi-repo spawn exposes the FULL owner/repo->worktree map via EGG_REPO_VOLUMES (JSON) + the ordered EGG_PIPELINE_REPOS (JSON list); EGG_PIPELINE_REPO stays the primary (first) repo for back-compat. Same-short-name repos under different owners (ownerA/foo vs ownerB/foo) get distinct keys (ruling #6, no bare-name collision). N=1 back-compat asserted.

TestReposZeroCollapseRatchet: a tokenize-based sweep (not a text grep, so comments/docstrings and the self.repos[0] primary accessor are ignored) that fails if any bare repos[0]/pipeline_repos[0] collapse reappears in orchestrator source. Allowlist = sandbox/egg_lib/sdlc_hitl.py (guarded by len(repos)==1). Includes a detector self-test so the ratchet can't rot into a no-op.

test_gateway_client owner/repo keying guard: create_worktrees round-trips owner/repo keys with no collision.

TDD-red pending coder convergence (verified green at integration by make test-all): the ratchet currently enumerates the 5 live collapse tokens, and the env-map tests KeyError on EGG_REPO_VOLUMES until the coder lands task-3-1/3-2. The other 3 tests (gateway keying + 2 ratchet self-tests) pass now. ruff + py_compile clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sk-3-3)

Coder (5601063) exposes the full owner/repo->container-path worktree map as a single EGG_PIPELINE_REPOS JSON dict (the plan's named example) rather than a separate EGG_REPO_VOLUMES map + list. Converge TestMultiRepoAgentEnv to that delivered, AC-4-satisfying interface: full owner/repo-keyed map + EGG_PIPELINE_REPO primary back-compat + N=1.

Non-blocking observation flagged in a test docstring: the coder derives map VALUES as /home/egg/repos/<bare-name>, so two same-bare-name repos share a container mount target (a host_path_mounts scheme property, broader than this slice's owner/repo re-key). The same-name test asserts only KEY distinctness and does not bless the value collision.

All 7 slice-3 tester tests pass against the merged coder impl (ratchet green: collapse sites removed). ruff + py_compile clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…overseer repo (task-3-3 AC1)

Address reviewer_contract + reviewer_code_holistic NACKs on tester v1: AC1 requires unit-level positive-resolution assertions at ALL THREE former repos[0]-collapse sites; only the spawner-env site was covered. The ratchet proves the collapse token is gone but not that resolution is CORRECT.

commit-authorship (BLOCKER, both reviewers): new TestResolveAuthorshipRepoPathPrimaryHint exercises the slice-3 EGG_PIPELINE_REPO primary-hint branch of _resolve_authorship_repo_path -- hint wins over egg-preference; non-matching owner in the slug proves resolution splits on bare repo NAME (split('/')[-1]) not owner ([0] would degrade silently); degrade paths preserved (hint absent/not-checked-out -> egg-preference; no egg -> first-alpha).

overseer-repo (contract AC1 / holistic minor): new test_overseer_repo_resolution.py asserts _spawn_overseer_agent resolves the overseer model from the PRIMARY (first) repo for a 2-repo pipeline and None for empty/absent (never IndexError). Housed in its own module, NOT test_phase_scoped_overseer.py, which gates its whole import on routes.pipelines._check_and_respawn_overseer -- a symbol absent from pipelines.py on this branch AND main, so that module is skipped repo-wide and an assertion there would skip, not execute. _spawn_overseer_agent imports cleanly.

Verified (sandbox has no network; make test/lint abort at uv sync): system pytest 9.1.1 (PYTHONPATH=orchestrator:shared) -> 6 new pass, 70 authorship pass, ruff + py_compile clean. make test-all on the integration branch is the gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jwbron
jwbron changed the base branch from egg/issue-3393/slice-2 to main July 2, 2026 20:57
@jwbron
jwbron merged commit b3c2791 into main Jul 2, 2026
26 of 31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant