Skip to content

[issue-3312][slice-3/4] Decompose gateway/gateway.py (10,648... - #3503

Merged
jwbron merged 6 commits into
mainfrom
egg/issue-3312-v2/slice-3
Jul 6, 2026
Merged

[issue-3312][slice-3/4] Decompose gateway/gateway.py (10,648...#3503
jwbron merged 6 commits into
mainfrom
egg/issue-3312-v2/slice-3

Conversation

@james-in-a-box

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

Copy link
Copy Markdown
Contributor

Decompose gateway/gateway.py (10,648 lines / 419 KB) into a sub-package; drop its allowlist entry; seam coverage in gateway/CLAUDE.md. Depends on the previous slice (single linear chain).

Base PR: #3489

What's in this PR

Commits (4):

.../agent-outputs/coder/slice-3-seam-table-for-documenter.md    |    60 +
 .egg-state/brc-history/issue-3312-v2-implement-slice-3.json     |  6101 +++++++++++++++
 .egg-state/brc-history/issue-3312-v2-implement-slice-3.md       |  5454 +++++++++++++
 gateway/CLAUDE.md                                               |    26 +-
 gateway/Dockerfile                                              |     5 +
 gateway/entrypoint.sh                                           |     9 +-
 gateway/gateway.py                                              | 10648 --------------------------
 gateway/gateway/__init__.py                                     |  1342 ++++
 gateway/gateway/__main__.py                                     |    15 +
 gateway/gateway/_confluence.py                                  |  1230 +++
 gateway/gateway/_gh_execute.py                                  |   763 ++
 gateway/gateway/_gh_ops.py                                      |  1083 +++
 gateway/gateway/_git_execute.py                                 |   800 ++
 gateway/gateway/_git_ops.py                                     |  1357 ++++
 gateway/gateway/_health.py                                      |   253 +
 gateway/gateway/_helpers.py                                     |   187 +
 gateway/gateway/_jira.py                                        |   847 ++
 gateway/gateway/_jira_writes.py                                 |   964 +++
 gateway/gateway/_proxy.py                                       |   946 +++
 gateway/gateway/_server.py                                      |   413 +
 gateway/gateway/_sessions.py                                    |   943 +++
 gateway/gateway/_worktree.py                                    |   741 ++
 gateway/tests/conftest.py                                       |    79 +-
 scripts/file-size-allowlist.yaml                                |     2 -
 24 files changed, 23560 insertions(+), 10708 deletions(-)

This slice

Decompose gateway/gateway.py (10,648 lines, STRUCTURAL OUTLIER, OVER BYTE CAP): Flask @app.route seam (#3312 slice-18 equivalent)

Files affected:

  • gateway/gateway.py
  • gateway/gateway/__init__.py
  • gateway/gateway/
  • scripts/file-size-allowlist.yaml
  • gateway/CLAUDE.md
  • gateway/Dockerfile
  • gateway/entrypoint.sh
  • gateway/tests/
  • tests/
Tasks (7) + acceptance criteria
  • task-3-1: External-importer audit (section-(d)) for gateway.py: ~35 files reference gateway.gateway / import gateway (incl. config_validator.py); re-run the audit live. Enumerate every @app.route handler and every externally-referenced helper (e.g. get_anthropic_client).
    • Acceptance criteria: Full route+symbol inventory captured; re-export set derived; referencing-file estimate re-verified against the live tree.
  • task-3-2: Step-0 git mv gateway.py -> gateway/init.py; green baseline commit (move only). Confirm the Flask app object and all @app.route registrations still resolve on import.
    • Acceptance criteria: Pure-move baseline commit green; Flask app + routes register identically.
  • task-3-3: Apply the routes-handling convention (non-negotiable Merge Phase 1.5 docs into main #8 / section-(f)): @app.route decorators stay on thin wrapper functions in init.py; wrapper bodies delegate to implementation functions in responsibility-grouped _.py submodules (e.g. _git_ops, _pr_lifecycle, _credentials/auth, _validation/policy, _checkpoint, _worktree). Do NOT move the decorators. Explicit per-symbol re-export barrel for every externally-referenced helper.
    • Acceptance criteria: All decorators remain in init.py; route URL->handler map unchanged; submodules hold the bodies; barrel re-exports every external symbol; patch('gateway.gateway.*') targets resolve.
  • task-3-4: Pre-allocate and verify cluster sizing: at ~10.6k lines several submodules will themselves approach the cap; further-split in-slice (section-(g), recursive barrel pattern) rather than adding any fresh allowlist entry. Confirm EVERY resulting module is under BOTH caps.
    • Acceptance criteria: No submodule over 1500 lines / 100KB; any further-split uses a nested barrel; zero new allowlist entries.
  • task-3-5: Drop gateway.py's allowlist entry; author the concrete gateway/ submodule layout rows in gateway/CLAUDE.md's seam table.
    • Acceptance criteria: Allowlist entry removed; gateway/CLAUDE.md carries the concrete submodule layout; lint ratchet passes.
  • task-3-6: R3 container-packaging + Flask-launch mitigation (SAME SLICE). gateway/Dockerfile:67 ships top-level modules via the NON-recursive glob COPY gateway/*.py ./. Converting gateway.py -> gateway/gateway/init.py needs TWO same-slice fixes handled together: (a) add COPY gateway/gateway/ ./gateway/ to gateway/Dockerfile (mirroring the git_client/worktree_manager lines) so the new package dir ships; (b) preserve the Flask launch: gateway.py defines app = Flask(__name__) (L486) and if __name__=='__main__': app.run(...) (L10639/L10647), started on port 9848 via gateway/entrypoint.sh; keep the same app object exported through the barrel and update the launch invocation / any import gateway consumer (incl. config_validator.py) so the server still starts. BUILD the gateway image and smoke-check the container starts and serves on 9848.
    • Acceptance criteria: gateway/Dockerfile copies the new gateway/gateway/ package; app is exported through the barrel and the launch path (entrypoint.sh) starts the Flask server on 9848 unchanged; built image passes a start/serve smoke check.
  • task-3-7: make lint + make test-all green at the slice boundary; mechanical patch-path rewrites in-slice; any latent bug surfaced is filed as a 'Part of Decompose 19 oversize Python source files to clear the file-size allowlist (refresh of #3111) #3312' follow-up, never bundled.
    • Acceptance criteria: Green; no behavior change in the diff.

Stack

egg and others added 4 commits July 4, 2026 02:47
…eline)

Step-0 bisectable baseline for the #3312 gateway.py decomposition (slice-3 of
#3312's continuation, the slice-18-equivalent Flask @app.route seam). Pure git
mv with no extraction: the sub-package __init__.py is the pre-move file with only
the mechanical package-form fixups, so the gateway.gateway module path and every
route/handler are unchanged. Cluster extraction lands in the next commit.

Package-form fixups (no behaviour change):
- Relative sibling imports .X -> ..X (the module is one level deeper now); each
  already has an absolute `from X import` except-fallback for flat/container mode.
- _shared_path / _config_path gain one .parent to keep the same targets.
- gateway/tests/conftest.py spec-loads the package (submodule_search_locations),
  registering it as both `gateway` and `gateway.gateway` so patch("gateway.gateway.*")
  and the flat `import gateway` both resolve — mirrors the git_client/worktree_manager
  slice-11/12 loaders.

Container packaging + Flask launch (R3, same slice):
- gateway/Dockerfile: explicit `COPY gateway/gateway/ ./gateway/` (the non-recursive
  `COPY gateway/*.py ./` no longer matches the package dir).
- gateway/gateway/__main__.py + entrypoint.sh `python3 -m gateway` (replacing
  `python3 gateway.py`) preserve the Flask launch on port 9848.
  NOTE: the in-image `python -c 'import gateway'` / serve-on-9848 smoke check
  could not run in this sandbox (no docker); COPY/launch follow the established
  git_client/worktree_manager pattern.

Part of #3312.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Second commit of slice-3 (the #3312 slice-18-equivalent Flask @app.route seam):
decompose the pure-move baseline into a barrel + 14 responsibility-grouped
submodules, all under the 1500-line / 100 KB caps (largest _git_ops.py, 1357
lines / 57.9 KB; barrel 1342 lines). Pure refactor — handler/helper bodies are
AST-identical to the pre-split file.

Routes-handling convention: every @app.route decorator stays on a thin wrapper
in the barrel (__init__.py); the wrapper delegates to the implementation in the
_<cluster> submodule. The barrel does explicit per-symbol re-exports and
declares __all__, so external importers and unittest.mock.patch targets
(patch("gateway.gateway.X") / patch.object(gateway, "X")) resolve unchanged.

Seam preservation:
- _b() accessor: submodules resolve *patched* seam getters/validators on the
  barrel at call time, so patch("gateway.gateway.<name>") stays effective;
  non-patched cross-submodule helpers use direct typed imports.
- _BarrelLogger proxy: submodule `logger` forwards to the barrel logger so
  tests patching gateway.logger observe submodule log calls.
- Module-singleton seams (gateway.subprocess.run / gateway.time.sleep /
  gateway.open) stay barrel attributes; patching the shared module is honoured
  process-wide.

Submodules: _helpers (responses/audit/connectivity), _health, _git_ops
(push/fetch), _git_execute, _gh_ops (PR lifecycle), _gh_execute, _jira (reads),
_jira_writes, _confluence, _worktree, _sessions, _proxy (Anthropic /v1/messages),
_server (main + health server).

Drops gateway.py's file-size-allowlist entry (the barrel + submodules are all
under cap); the files: map now holds only orchestrator/routes/pipelines.py
(slice-4's target). Seam-table data for gateway/CLAUDE.md handed to the
documenter via .egg-state/agent-outputs/coder/slice-3-seam-table-for-documenter.md.

Verification: full gateway/tests suite passes (3342 passed; the 7 failures + 6
errors are all pre-existing sandbox-env failures — git-init-blocked
git_client/worktree_manager tests and squid-403 health-server tests, identical
to the pre-split baseline). ruff check + format clean.

Part of #3312.

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

Add the gateway/gateway/ subsection to gateway/CLAUDE.md's Decomposition
seams table (task-3-5 doc portion): barrel + 14 submodules, routes convention
(@app.route decorators stay on thin barrel wrappers delegating to _<cluster>
impls), barrel re-export/__all__ + _b()/_BarrelLogger/module-singleton seam
mechanism, and the R3 Dockerfile COPY + python3 -m gateway launch note.
Stacks on the coder's decomposition at 3a85636.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@james-in-a-box james-in-a-box Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contract Verification — PR #3503 (issue-3312-v2, slice-3/4)

Verified the gateway.py decomposition against the 7 slice-3 tasks and their acceptance criteria. Reviewed the working tree at head 711b0de and exercised the imports directly. Result: structurally sound, all code-level criteria met — one AC (task-3-6 container smoke check) could not be executed in-sandbox and needs CI/human validation before merge.

Task-by-task

task-3-1 — External-importer audit / re-export set
Barrel re-exports a full public surface (__all__ = 187 entries). Spot-checked externally-referenced symbols resolve as barrel attributes: get_anthropic_client, GitHubClient, WorktreeManager, audit_log, logger, subprocess, time, make_response. Seam-table artifact produced for the documenter.

task-3-2 — Pure-move baseline; app + routes register identically
import gateway.gateway yields a Flask app with all routes registered (61 url_map rules incl. /api/v1/health, /api/v1/git/push). Old gateway/gateway.py is removed. (The intermediate move-only commit tree isn't inspectable from this single-commit worktree, but the final state is consistent with the PR's 4-commit structure.)

task-3-3 — Routes-handling convention
All 50 real @app.route decorators live in __init__.py; the 13 matches outside the barrel are module-docstring references, not decorators. Wrappers are thin and delegate to _<cluster> impls (e.g. git_push()_git_ops.git_push()). patch("gateway.gateway.<symbol>") targets resolve; conftest registers the package under both gateway and gateway.gateway so flat + dotted paths both resolve.

task-3-4 — Cluster sizing under caps
Every submodule is under both caps. Largest: _git_ops.py 1357 lines / 57.9 KB; barrel __init__.py 1342 lines / 44.7 KB. No submodule > 1500 lines or 100 KB; no nested further-split needed; zero new allowlist entries.

task-3-5 — Drop allowlist entry + CLAUDE.md seam doc
gateway.py removed from scripts/file-size-allowlist.yaml; only orchestrator/routes/pipelines.py (slice-4's target) remains. gateway/CLAUDE.md gains a concrete gateway/gateway/ seam section documenting the barrel + 14 submodules, the routes convention, and the _b()/_BarrelLogger/module-singleton seam mechanism.

task-3-6 — Dockerfile COPY + Flask launch ⚠️ mostly satisfied, one unrunnable AC

  • gateway/Dockerfile:80 adds COPY gateway/gateway/ ./gateway/
  • Launch converted to python3 -m gateway --host 0.0.0.0 --port 9848 in entrypoint.sh (both gosu and fallback paths), via __main__.py → barrel main()
  • app and main are exported through the barrel (main in __all__; app is a module attribute, importable as from gateway.gateway import app ✅ — see nit below)
  • config_validator.py does not import gateway, so no consumer update was needed ✅
  • Gap: the AC requires "built image passes a start/serve smoke check". The commit message states the in-image import gateway / serve-on-9848 check could not run (no docker in sandbox). The COPY + launch changes mirror the already-landed git_client/ and worktree_manager/ patterns and both import paths verify locally, so risk is low — but the container build/serve check is not evidenced. Flagging for CI or a human reviewer to confirm the image builds and serves on 9848 before merge.

task-3-7 — Green + no behavior change ⏳ deferred to CI
Per review conventions I did not run make test-all. Coder reports 3342 passed with 7 failures + 6 errors that are pre-existing sandbox-env failures (git-init/squid-403), and claims AST-identical bodies. Import smoke test + wrapper→impl delegation support no-behavior-change; CI is authoritative for the green gate.

Minor nit (non-blocking)

gateway/CLAUDE.md states the barrel __all__ keeps app in the public API, but app is not literally in the __all__ list (only main is). Functionally harmless — from gateway.gateway import app resolves regardless (__all__ only governs import *, which nothing here uses) — but the doc slightly overstates it.

Contract bookkeeping note

The contract (issue-3312-v2) still shows slice-3 and all 7 tasks as pending with phase refine and no linked commits, and has no top-level ac-N acceptance criteria (empty array). So there were no ac-N entries to mark via verify-criterion, and the orchestrator was unreachable. Verification above is against each task's acceptance_criteria string. The contract task/phase status was not advanced as the work landed — worth reconciling.

Verdict

No correctness defects found; the decomposition is a clean, cap-compliant refactor that matches the contract. The only outstanding item is the unexecuted container start/serve smoke check (task-3-6) — a sandbox limitation, not a code defect — which CI/human review should confirm before merge. Posting as a comment (bot self-authored PR).

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

@james-in-a-box

james-in-a-box Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Feedback addressed — PR #3503

Thanks for the contract verification. Per-item disposition:

1. Minor nit — gateway/CLAUDE.md states __all__ keeps app in the public API, but app is not literally in __all__ (only main is).fixed-in-PR (commit 64fa307)
Rather than weaken the doc, I made the code match the documented intent: app (the Flask app) is a genuine public symbol — imported by the entrypoint (python3 -m gatewaymain()) and by tests via from gateway.gateway import app, and re-exported through the barrel by design. Added "app" to __all__ (sorted correctly between _worktree_prune_lock and artifact_bp). The barrel now literally exports the full public API the doc describes, and import * behaviour is now consistent with the doc. ruff check passes on the file.

2. task-3-6 — container start/serve smoke check could not run in-sandbox (no docker); flagged for CI/human confirmation.disagree (not a code defect; nothing to change)
This is a verification gap, not a code issue. The COPY gateway/gateway/ ./gateway/ (Dockerfile:80) and the python3 -m gateway --host 0.0.0.0 --port 9848 launch (both gosu + fallback paths in entrypoint.sh) mirror the already-landed git_client/ and worktree_manager/ packaging patterns, and both import paths verify locally. There is no code change I can make to close this — CI's image build/serve on this PR HEAD is the authoritative confirmation, exactly as the review requests. No edit required.

3. Contract bookkeeping — issue-3312-v2 still shows slice-3/tasks as pending, no linked commits, empty ac-N array; "worth reconciling".disagree (out of the PR diff; nothing to change in this PR)
This is SDLC-contract state living outside the PR's code diff, not something the branch content can fix. There were no ac-N acceptance criteria to verify-criterion (empty array), and the reviewer noted the orchestrator was unreachable. Reconciling contract/phase status is an orchestrator-side action independent of this PR's code — no code change here would move it. Surfacing for the operator to reconcile the contract state; nothing to edit in the diff.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box james-in-a-box Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contract Verification (re-review) — PASS

Re-verified slice-3 (gateway/gateway.py decomposition) at 64fa3077. The delta since my last review (711b0de) is a single one-line change: "app" added to __all__ in gateway/gateway/__init__.py, in correct alphabetical position (after _worktree_prune_lock, before artifact_bp).

Delta assessment

  • Correct and minimal. app is defined in the barrel (app = Flask(__name__), __init__.py:513) and is now an explicit public export. This matches the documented public API in gateway/CLAUDE.md:80 ("keeping the full public API (app, main, …) as the stable surface") — the code now agrees with the doc.
  • Directly satisfies task-3-6 AC ("app is exported through the barrel").
  • No regression risk. Purely additive to __all__. Import smoke test: package imports cleanly, g.app is a Flask instance, g.main is callable, and every one of the ~200 names in __all__ resolves as an attribute (0 dangling) — the new app entry does not dangle.

Previously-verified criteria re-confirmed (no regressions)

  • task-3-3 (decorators stay in barrel): all 49 real @app.route decorators are in __init__.py; zero in the cluster submodules (the 13 submodule matches are identical docstring lines). ✓
  • task-3-4 (sizing): every submodule under both caps — largest is _git_ops.py at 1,357 lines / 57.9 KB (< 1,500 lines / 100 KB). ✓
  • task-3-5 (allowlist + seam doc): gateway.py entry absent from scripts/file-size-allowlist.yaml; gateway.py file removed; gateway/CLAUDE.md carries the concrete gateway/gateway/ submodule seam table. ✓
  • task-3-6 (packaging + launch): gateway/Dockerfile:80 adds COPY gateway/gateway/ ./gateway/; entrypoint.sh launches python3 -m gateway --host 0.0.0.0 --port 9848__main__.pymain() (in _server.py) → app.run(port=9848). Port 9848 unchanged. ✓

Notes

  • No top-level ac-N acceptance criteria exist in this contract (criteria are per-task strings within the slice), so there are no verify-criterion ids to mark.
  • The orchestrator was unreachable during this review, so contract state was read from the frozen worktree snapshot (.egg-state/contracts/issue-3312-v2.json); code was verified directly against the tree.
  • I did not run make test-all (per review policy — left to CI).

Verdict: No contract violations. The delta is a correct, self-contained public-API fix. Posting as a comment per the self-authored-PR convention.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

@james-in-a-box

james-in-a-box Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Feedback addressed — PR #3503

Thanks for the re-review. The latest contract verification at 64fa3077 is a PASS with no contract violations and no new actionable items — it confirms the app-in-__all__ fix and re-verifies every prior criterion with no regressions. Per-item disposition:

1. Re-review delta (app added to __all__, alphabetical position, matches gateway/CLAUDE.md:80).disagree (no action needed — this is the reviewer confirming the fix already in this PR)
The one-line delta reviewed here is commit 64fa307, which I pushed in the previous round to address the original nit. The re-review verifies it is correct, minimal, and regression-free (g.app is a Flask instance, all ~200 __all__ names resolve, 0 dangling). Nothing further to change.

2. Notes — no top-level ac-N criteria to verify-criterion; orchestrator unreachable so contract read from frozen snapshot; make test-all left to CI.disagree (non-actionable observations outside the PR diff)
These are the same environmental/bookkeeping notes dispositioned in my prior response. There are no ac-N entries to mark (empty array), the orchestrator is orchestrator-side state independent of this branch's code, and CI is the authoritative green gate per review policy. No code change in this diff can move any of these.

No code changes were required for this round — the re-review is a confirmation of the already-pushed fix. PR HEAD remains 64fa3077.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@jwbron
jwbron changed the base branch from egg/issue-3312-v2/slice-2 to main July 6, 2026 16:50
@jwbron
jwbron merged commit 632db67 into main Jul 6, 2026
13 of 15 checks passed

@james-in-a-box james-in-a-box Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contract re-verification — incremental (delta since 64fa307)

Verdict: Approve (comment). The delta since my last review is a single merge commit (26400837) that merges main into the slice-3 branch and resolves the scripts/file-size-allowlist.yaml conflict. It preserves all previously verified work and introduces no contract violations. (PR is already merged; this is a confirming record.)

Delta reviewed

64fa3077..26400837 — the merge "Merge main into slice-3; drop gateway.py allowlist entry, keep concurrent_executor.py". The only PR-authored change in the merge is the allowlist conflict resolution; the remaining 87 commits are main history pulled in by the merge.

Conflict resolution is correct

scripts/file-size-allowlist.yaml files: now contains:

  • orchestrator/routes/pipelines.py — untouched (slice-4 scope, not this PR)
  • orchestrator/concurrent_executor.pykept (main's addition) ✓
  • gateway/gateway.pyremoved (this slice's deliverable) ✓

This is exactly the resolution the commit message describes: take main's concurrent_executor.py entry, keep this branch's removal of the gateway.py entry. No cross-contamination.

Previously verified work still holds

  • gateway/gateway.py decomposed into gateway/gateway/ (barrel __init__.py + 14 sub-modules). All modules are within caps (max 1357 lines / 57.9 KB vs hard_lines: 1500 / hard_bytes: 100000), so dropping the allowlist entry is legitimate — no file is over cap.
  • Barrel re-exports intact: from ._confluence/._gh_ops/._git_ops/... with # noqa: E402,F401; app = Flask(__name__) and get_anthropic_client exposed. "app" is present in __all__ (the 64fa307 fix survived the merge).
  • Dockerfile COPY parity (R3): COPY gateway/gateway/ ./gateway/ (Dockerfile:80) with the explanatory comment; entrypoint launches via python3 -m gateway (entrypoint.sh:343/352), matching gateway/__main__.py.
  • No merge-conflict markers left in any PR-authored file.

Acceptance criteria

Contract top-level acceptance_criteria is empty ([]), so there are no ac-N entries to mark via verify-criterion. All four slices in issue-3312-v2.json (models.py, event_loop.py, gateway.py, pipelines.py) are status: complete; this PR covers the gateway slice, whose per-task criteria (external-importer re-export audit, byte-cap compliance, Dockerfile parity) are met as verified above.

No blocking issues.

— Authored by egg

@james-in-a-box

james-in-a-box Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

egg contract-verification completed. View run logs

6 previous review(s) hidden.

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