[issue-3393][slice-3/6] Stop the repos[0] collapse... - #3424
Merged
Conversation
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>
…into egg/issue-3393-slice-3-tester/work
…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
approved these changes
Jul 2, 2026
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.
Remove all three enumerated
repos[0]collapse sites and expose the full repo→worktree map to the agent environment, re-keyed by fullowner/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 agrep -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):
This slice
Stop the repos[0] collapse; owner/repo-keyed worktree map in agent env
Files affected:
orchestrator/kubernetes_spawner/_spawn.pyorchestrator/commit_authorship_store.pyorchestrator/routes/pipelines.pyorchestrator/gateway_client/_worktree.pygateway/gateway.pyorchestrator/tests/test_kubernetes_spawner.pyorchestrator/tests/test_gateway_client.pyTasks (3) + acceptance criteria
repo_name = repos[0].split("/")[-1]) and :464 (pipeline_repo = repos[0]) plus theEGG_PIPELINE_REPOexport 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 fullowner/repo(feeding from the clientworktreesfield → spawnerrepo_volumes). KeepEGG_PIPELINE_REPOpopulated with the primary for back-compat but add the full map (e.g. anEGG_PIPELINE_REPOS/ repo_volumes env exposure) so agents can select the slice's repo. (b) orchestrator/commit_authorship_store.py:932-933 — replace therepos[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 theprimary_repoaccessor from slice 1 rather than re-introducing index-0 access._spawn.py:452/:464,commit_authorship_store.py:932-933,pipelines.py:732) no longer indexrepos[0]; they use the primary accessor or the full map. - The agent environment exposes the full repo→worktree map keyed by fullowner/repo(no bare-name collision). -EGG_PIPELINE_REPOstill resolves to the primary for back-compat.owner/repoat its source. In orchestrator/gateway_client/_worktree.py:74-79 theworktreesdict is keyed by bare repo name (repo.split("/")[-1]-shaped); change the key to the fullowner/reposo 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 viarepo_volumes) to read the owner/repo keys.WorktreeResult.worktrees(and the gateway handler that builds it) is keyed by fullowner/repo. - Same-name-different-owner repos get distinct entries. - The spawner reads the owner/repo-keyed map; paths are unchanged.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.repos[0]collapse is reintroduced, with sdlc_hitl.py:82 allowlisted. -make test(narrowed) andmake test-allgreen.Stack
issue-3393egg/issue-3393/slice-2