C7 of the domain-reorg adoption (build plan docs/upstream-sync/domain-reorg/ reorg-build-plan.md, step C7): - CLAUDE.md / AGENTS.md: retarget architecture path references to the new layout — graph entrypoints via agent.graphs.* shims, the agent/api/ + agent/webhooks/*_routes.py FastAPI split (webapp.py now a shim), agent/review/ package, tests/<domain>/ test paths, and the new-graph/ test conventions. README.md already pointed at docs/ (C1) — no change. - triage.jsonl: flip 8356eb34 (#1726) to landed on branch refactor/domain-reorg-adoption; add re-triage notes to the 8 unblocked rows (#1732/#1761/#1744/#1748/#1742 clean, #1736/#1758/#1760 near-clean). triage.md regenerated via make triage-render. - Ship the plan, scoping report, move-map artifacts, and the domain-reorg-adoption workflow so the exercise is reproducible. Memory + Confluence handled out-of-band (not in this commit): project memory updated with the new module layout; Confluence check found no IT page documents the repo module map — no Confluence change required.
25 KiB
Scoping report — adopting upstream domain reorg 8356eb34 (#1726) as a file-move exercise
Date: 2026-07-17. Repo: /Users/adammoussa/Documents/repositories/seahaven/open-swe (branch dev, clean).
Strategy scoped: fork file contents, upstream layout — replay the reorg's moves onto the fork's own tree; never take upstream file contents.
Merge-base with upstream/main: f6c215ff. Reorg commit sits 91 upstream commits past the merge-base.
Current truthful conflict surface (git merge-tree --write-tree --name-only HEAD upstream/main): 140 conflicted paths (docs/upstream-sync/domain-reorg/merge-tree-now.txt).
1. Move-map (git diff-tree -r -M50 --name-status 8356eb34^ 8356eb34)
298 entries total (raw file: docs/upstream-sync/domain-reorg/movemap-m50.txt):
| class | count | meaning |
|---|---|---|
| R100 pure renames | 150 | byte-identical moves (mostly tests/* → tests/<domain>/, ui/src/{components,lib}/agents/* → ui/src/features/*, agent/reviewer_*/review_style_* → agent/review/) |
| R<100 rename+edit | 77 | scores R062–R099; every sampled edit is an import-path / monkeypatch-target rewrite only (see §Spot-checks) |
| M in-place edits | 49 | shim-ification (webapp.py 2007-line reduction, server.py constant extraction), import rewires in agent/tools/*, agent/webhooks/*, evals/, ui/src/routes/*, config files |
| A true adds | 21 | all thin structure: agent/graphs/{agent,analyzer,chat,reviewer,scheduler}.py (re-export shims), agent/runtime/{constants,execution,sandbox}.py (constants + delegating wrappers around agent.server), agent/api/{app,health}.py, agent/webhooks/{common,github_routes,linear_routes,slack_routes}.py, agent/{providers,resources,review,runtime,graphs,api}/__init__.py |
| D true deletes | 1 | ui/src/components/agents/AgentPromptBar.tsx — a 2-line re-export shim, re-added at ui/src/features/agents/components/AgentPromptBar.tsx with the path retargeted (A/D pair is cosmetic, not a content loss) |
Content-consolidations (real redistribution rather than move): effectively one — the FastAPI layer.
agent/webapp.py (upstream pre-reorg ~2k lines) is split into agent/webhooks/common.py (shared dispatch/verify helpers), agent/api/app.py (composition), agent/api/health.py (/health + /webhooks/run-complete), and per-source *_routes.py; webapp.py becomes a 4-line compatibility shim (from .api.app import app). Secondary micro-extractions: server.py → runtime/constants.py (DEFAULT_LLM_MODEL_ID, DEFAULT_LLM_MAX_TOKENS, DEFAULT_RECURSION_LIMIT, MODEL_CALL_RECURSION_LIMIT) and runtime/execution.py (graph_loaded_for_execution); prompt.py → default_prompt.md loaded via importlib.resources from agent/resources/.
The "consolidate reviewer modules" sub-commit is not a content merge — it is nine 1:1 module moves into agent/review/ (R098–R100) plus import rewires.
langgraph.json does change: graph entrypoints retarget from agent.server:traced_agent / agent.reviewer:... / agent.analyzer:... / agent.chat:... / agent.scheduler:... to agent.graphs.<name>:<same symbol>. The targets are pure re-export shims (e.g. agent/graphs/agent.py = from agent.server import get_agent, traced_agent), so behavior is unchanged, but the deployment manifest strings are not. http.app stays agent.webapp:app (via the compat shim). See §Hazards (d).
2. Collision set (move-map × fork divergence)
Fork divergence since merge-base: 322 paths (130 A / 188 M / 3 D / 1 R; raw: docs/upstream-sync/domain-reorg/fork-divergence-status.txt). Fork-added files split against upstream trees: 67 are shared (cherry-picked, exist upstream pre-reorg → move-map applies) and 64 are TRUE fork-only (exist nowhere upstream).
Headline classification:
| bucket | count |
|---|---|
MECHANICAL — fork-touched, R100 pure rename → git mv fork's version |
57 |
MECHANICAL-trivial — moved by reorg, fork never touched → git mv (identical either way) |
127 |
HAND-CARRY (light) — fork-touched, R<100 → git mv + replicate upstream's import-rewrite pattern on fork content |
34 |
| HAND-CARRY (real) — fork-modified AND reorg-modified-in-place (M×M) | 34 (≈10 substantive, rest are 1–3-line import seds) |
| FORK-ONLY placement decisions | 64 files, of which 13 test files need explicit new homes; nearly all fork-only source stays put (reorg does not move agent/tools/, agent/utils/, agent/middleware/, agent/integrations/, agent/dashboard/, agent/webhooks/ handler modules) |
| moved upstream but absent from fork (added in the 91 interim commits — no-op for us) | 9 |
2a. MECHANICAL (fork-touched, pure rename — the bulk)
git mv fork's file to the new path; no content change. 57 files:
- Root/docs/resources:
INSTALLATION.md → docs/INSTALLATION.md,default_prompt.md → agent/resources/default_prompt.md(pairs with theprompt.pyloader hunk — see HAND-CARRY),agent/reviewer_findings.py → agent/review/findings.py. - Tests (46): the full
tests/test_*.py → tests/<domain>/test_*.pyset, e.g.tests/test_dispatch.py → tests/agent/,tests/test_workflow_push_guard.py → tests/agent/,tests/test_plan_mode.py,tests/test_plan_review.py → tests/agent/,tests/test_auth_sources.py,tests/test_authorship.py,tests/test_encryption*→ tests/auth/,tests/test_github_app.py,tests/test_open_pull_request.py,tests/test_pr_creation_guard.py → tests/github/,tests/test_gateway.py,tests/test_proxy_auth.py,tests/test_local_integration.py → tests/sandbox/, sanitize/middleware tests →tests/middleware/, model tests →tests/models/, Slack tool tests →tests/slack/, dashboard/team-settings tests →tests/dashboard/,tests/test_completion_webhook.py → tests/webhooks/, etc. (full list indocs/upstream-sync/domain-reorg/cross.json, keymech). - UI (10):
ui/src/lib/agents/{api,queries,types,streamMessagesToUi,sidebarFilter,messageTimestamps}.ts → ui/src/features/agents/lib/,ui/src/components/agents/PrHeader.tsx → ui/src/features/reviews/components/,MessageTimestamp.tsx,diffUtils.ts.
Plus the 127 fork-untouched moved files (same treatment, zero divergence risk).
2b. HAND-CARRY
(i) R<100 moved files fork also touched — 34 files. Upstream's edits are exclusively path rewires; replicate the same rewires on the fork's content after git mv:
- Python:
agent/reviewer_publish.py → agent/review/publish.py(R099:from .reviewer_findings import→from .findings import,from .utils.X→from ..utils.X), same forreviewer_reconcile.py,reviewer_trace_context.py; reviewer test files R062–R099 retargetwebapp→webhook_common/github_webhooksmonkeypatch modules;tests/test_e2b_integration.py → tests/sandbox/(fork re-implemented E2B sync — keep fork content, fix imports only). - UI: 20
.tsx/.tsfiles where the only edits are@/components/agents/...→@/features/agents/components/...and@/lib/agents/...→@/features/agents/lib/...(verified zero non-import edits on the worst-looking case,AgentThreadView.tsxR092). Fork-diverged files in this set that must keep fork content:PlanReview.tsx(plan-mode #130),WorkflowApprovalCard.tsx,AgentsSidebar.tsx,SidebarFilterMenu.tsx,AgentGitPanel.tsx,AutomationEditor.tsx.
(ii) M×M in-place overlap — 34 files, of which the substantive ten:
| file | upstream's reorg edit | fork action |
|---|---|---|
agent/webapp.py (fork: 2,590 lines) |
gutted to 4-line shim; content → webhooks/common.py + api/app.py + api/health.py + *_routes.py |
the big one — split the fork's monolith the same way, carrying fork-only Jira/Confluence/Connect routes (see §3) |
agent/server.py |
extract 4 constants → runtime/constants.py, graph_loaded_for_execution → runtime/execution.py; adds public aliases get_cached_sandbox_backend etc. |
replicate extraction on fork content; fork's sync sandbox lifecycle stays in server.py/utils/sandbox_state.py untouched — runtime/sandbox.py shim delegates to agent.server (upstream's own shim does exactly this, so it wraps fork code cleanly) |
agent/prompt.py |
DEFAULT_PROMPT_PATH env-only + importlib.resources load of agent/resources/default_prompt.md |
apply this one hunk by hand to fork's heavily customized prompt.py; verify the wheel packages the .md (upstream did not touch pyproject.toml; packages=["agent"] under hatchling should carry it — build and check) |
agent/reviewer.py / agent/analyzer.py / agent/chat.py |
import rewires to agent.review.* / small |
sed-level on fork content |
agent/webhooks/{github,linear,slack}.py |
from agent import webapp → from . import common; webapp.X → common.X (module-attribute style preserved so monkeypatching keeps working) |
caution: these exist on both sides with different content (fork built its own split earlier). No git mv applies — sed the fork's files; never adopt upstream's |
tests/conftest.py |
from agent import webapp → from agent.webhooks import common as webhook_common (auto-review fixture) |
same sed on fork's conftest |
tests/e2e/harness.py |
from agent.webapp import app, generate_thread_id_from_slack_thread → from agent.api.app import app + from agent.utils.thread_ids import ... |
fork already has agent/utils/thread_ids.py — same retarget |
ui/tsconfig.json / ui/eslint.config.js |
replace the 8-file ported/ exclude lists with src/features/agents/experiments/** |
adopt the glob (simplification, no behavior change); ui/vite.config.ts upstream edit is cosmetic churn on the fork-only mock-harness plugin — skip |
langgraph.json |
entrypoints → agent.graphs.* |
retarget fork's file and add ci_monitor": "agent.graphs.ci_monitor:get_ci_monitor" shim for the fork-only graph (upstream deleted its ci-autofix cluster; fork keeps it) |
AGENTS.md/CLAUDE.md/README.md |
doc-path updates | fork-rewritten docs — update path references manually (CLAUDE.md architecture sections name agent/webapp.py, tool/test paths) |
The remaining ~24 M×M files (agent/tools/add_finding.py and the other reviewer tools, agent/dashboard/{eval_jobs,review_api,review_chat_api}.py, evals/reviewer/*, ui/src/routes/*.tsx, agent/utils/github_org_membership.py, agent/middleware/settle_review_check.py) take 1–3-line import seds (..reviewer_findings → ..review.findings, @/components/agents → @/features/...).
Blast-radius counts (fork tree): 38 Python files import reviewer_*/review_style_* modules; 12 Python files import agent.webapp; 240 monkeypatch.setattr(webapp, ...) sites across 11 test files must retarget to webhook_common/handler modules; 54 UI files import @/components/agents or @/lib/agents.
2c. FORK-ONLY placement proposals (64 true fork-only files)
Upstream's domain logic: tools stay flat in agent/tools/; util clients in agent/utils/; webhook handlers in agent/webhooks/<source>.py with HTTP routes in agent/webhooks/<source>_routes.py; tests in tests/<domain>/.
Source — stays put (no move needed):
- Atlassian tools
agent/tools/{jira_*,confluence_*}.py(11 files) → stay (agent/tools/unmoved by reorg). agent/utils/{jira,confluence,adf,atlassian_connect,jira_project_repo_map,confluence_space_repo_map}.py→ stay.- Fork middleware,
agent/integrations/e2b.py, dashboard additions,.githooks/,.github/,scripts/,docs/upstream-sync/,docs/repo-conventions/,deploy/→ stay.
Source — new homes created by the split (part of the webapp-split commit):
agent/webhooks/jira.py,agent/webhooks/confluence.py→ stay as handler modules; add fork-onlyagent/webhooks/jira_routes.pyandagent/webhooks/confluence_routes.pymirroring upstream'sgithub_routes.pypattern (routes access helpers viacommon.X).- Atlassian Connect lifecycle/descriptor routes (
/connect/*,GET /connect/atlassian-connect.json) → proposeagent/webhooks/confluence_routes.py(they are Confluence-trigger plumbing) or a dedicatedagent/webhooks/connect_routes.pyif cleaner; register in fork'sagent/api/app.py.
Tests — 13 fork-only/uncovered files:
| file | proposed home |
|---|---|
tests/test_jira_webhook_{author,corroboration,replay}.py, tests/test_confluence_webhook.py, tests/test_linear_webhook_replay.py |
tests/webhooks/ |
tests/test_atlassian_connect.py |
tests/auth/ (JWT/qsh verification surface; alternative tests/webhooks/) |
tests/test_jira_utils.py, tests/test_confluence_utils.py |
tests/tools/ (client layer backing the Jira/Confluence tools) |
tests/test_repo_binding_isolation.py (TID-COLLIDE-01 guard) |
tests/sandbox/ (it exercises the sandbox/GitHub-token per-thread caches; alternative tests/webhooks/) |
tests/test_auth_error_leak.py |
tests/auth/ |
tests/test_app_bot_identity.py |
tests/github/ (bot authorship/PR attribution) |
tests/test_ci_autofix.py, tests/test_autofix_webhook.py |
tests/github/ (upstream deleted the impl cluster; fork keeps agent/ci_autofix.py, agent/ci_monitor.py in place) |
UI fork-only: ui/src/components/PwaUpdateToast.tsx → ui/src/components/ stays (not an agents-feature file); ui/src/lib/auth-redirect*.{ts,tsx} → stay in ui/src/lib/ (upstream kept non-agents lib files there); ui/src/routes/$owner.$repo.pull.$number.tsx → stays (routes/ not moved). tests/e2e/** (fork harness + specs) → stays; upstream kept tests/e2e/ in place.
3. Consolidations / decision list for Adam
- FastAPI layer split (the only true consolidation) — FLAG-HUMAN, auth surface. Fork's 2,590-line
agent/webapp.pycarries fork-only webhook auth:verify_jira_secret(+ optional HMAC/timestamp), Atlassian Connect JWT/qsh handling, the GitHub/Slack/Linear signature paths, token-attribution gating, TID-COLLIDE-01 thread-binding. Splitting it intowebhooks/common.py+api/+*_routes.pymoves signature-verification and dispatch code across files. Recommendation: adopt the split (it is behavior-preserving by construction — upstream kept module-attribute access precisely so monkeypatched tests stay valid, and the fork's webhook tests are the safety net), but treat the split commit as a sensitive change: /sh-security-review required (auth/webhook surface), and per the fork playbook surface the resolution choice to you before merge. No GPT-4.1 cross-family review strictly triggered (no IAM/Lambda-signature change), but the split of webhook verification code is a reasonable opt-in candidate. langgraph.jsonentrypoint retarget — deployment-manifest change (graphs →agent.graphs.*shims;http.appunchanged). Verify the LangGraph deployment redeploys cleanly; decide whether fork-onlyci_monitorgets aagent/graphs/ci_monitor.pyshim (recommended, for symmetry) or keeps its old path (inconsistent).- Reviewer consolidation →
agent/review/package: adopt. It is 9 pure moves + import rewires; fork's reviewer customizations (#1713 reconciliation, findings model, publish flow) ride along untouched. No content decision needed. - CI-autofix cluster (upstream deleted
agent/ci_autofix.py; fork keeps it): confirm keep-baseline, and approve its post-split wiring point (its webhook triggers live in the splitgithub.py/common.py) + test placement (tests/github/). - UI
experiments/adoption: upstream renamesported/→features/agents/experiments/+chat/and swaps the 8-file tsconfig/eslint exclude lists for one glob. Adopt (pure simplification). Fork-divergedDiffView.tsx,CloudPromptBar.tsx, etc. keep fork content. - Atlassian placements (§2c): approve
jira_routes.py/confluence_routes.pynaming and where Connect lifecycle routes land — this is fork-only auth surface; your call onconnect_routes.pyvs folding intoconfluence_routes.py. - Sequencing vs #1736 (bind cached GitHub tokens to users — priority security pick): recommend adopting the reorg first, because #1736 is written against
agent/webhooks/common.py, which only exists after the split; it then lands near-clean (still gated by cross-review + /sh-security-review per its ledger row).
4. Hazards check (CLAUDE.md playbook)
- (a) Thin-router re-import rebinds: post-reorg upstream is clean —
git grep 'from \.webhooks\.' 8356eb34 -- agent/webapp.py agent/api agent/webhooks/common.pyreturns nothing; routes access handlers viafrom . import github as service+service.X/common.Xmodule-attribute style, so the rebind hazard the playbook documents is designed out. Residual risk only if a hand-carry file drifts toward upstream content:agent/webhooks/{github,linear,slack}.pyexist on both sides with different content (fork versions carry the fixes) — taking upstream's file here silently drops fork behavior. Same fortests/conftest.pyandtests/e2e/harness.py. Rule for the exercise: upstream content may only be taken for the 21 "A" structural shims, everything else moves fork content. - (b) Paired tests move with impls: the map's test moves are 1:1 with unmoved impls (impl modules stay importable at old or shimmed paths), so no vertical splits arise; the 13 uncovered fork tests get the placements in §2c. Keep
tests/test_e2b_integration.py(fork's sync E2B) as fork content when moving totests/sandbox/.tests/middleware/test_sandbox_recovery.py→tests/sandbox/(fork hastests/middleware/? — it exists only as the upstream pre-path; fork's copy sits wherever it was cherry-picked; include in the mechanical sweep). - (c) Tooling path references: verified —
Makefile: only generictests/(TEST_FILE ?= tests/) — unaffected;make triage-render/-checkscripts don't reference moved paths.pyproject.toml:packages = ["agent"],testpaths = ["tests"]— both recursive, unaffected; verify wheel includesagent/resources/default_prompt.md(only real packaging check)..github/workflows/ci.yml: referencestests/e2e/**(unmoved) andui/bun checks — unaffected;reviewer-eval.yml→evals/(unmoved).langgraph.json— changes (see decision 2); fork-onlytests/e2e/langgraph.e2e.jsonuses its ownagent_entrypoint.py(importsagent.server:traced_agent, which remains valid) — no change needed.- UI:
@alias root (src/) unchanged;vite.config.ts/tsconfig.json/eslint.config.jsneed only the exclude-glob swap; bun stays — do not import upstream's pnpm bits (fork oddly carries aui/pnpm-workspace.yaml; consider deleting it in this exercise as lockfile hygiene, per playbook rule 7). scripts/,.githooks/: no moved-path references found.
- (d) Public entrypoints: upstream's claim "without changing its public entrypoints" is true behaviorally, false textually —
langgraph.jsongraph strings all change toagent.graphs.*(verified by diff). HTTP surface (agent.webapp:app) and all webhook URLs unchanged. Fork must mirror the graph-string change plus itsci_monitorextra.
5. Payoff — the 20 deferred ledger rows (docs/upstream-sync/triage.md)
Of the 19 deferred rows besides 8356eb34 itself:
- Clean picks after adoption (path remap was the blocker): 5 — #1732 dashboard label rendering, #1761 capitalize labels, #1744 collapse git panel, #1748 Linear issue search (test-path remap), #1742 defensive copy.
- Near-clean after adoption (small content deltas or review gates remain): 3 — #1736 GitHub token binding (biggest beneficiary: written against
webhooks/common.py; auth gates still apply), #1758 sandbox-create JSON env, #1760 separate LangSmith key/endpoint (both additive to divergedintegrations/langsmith.py— small reconciles). - Still content reconciliation for fork-divergence reasons: 9 — #1724 (Slack NL plan approvals), #1731 (OpenAI Responses history vs #155 middleware), #1734 (Slack DMs), #1737 (reviewer diff bounds), #1741 (SSRF image fetches), #1733 (todos default-off vs fork prompt), #1735 (untagged two-party Slack), #1747 (Slack run links), #1719 (web-tool offload vs sync sandbox lifecycle). Adoption still removes their test/UI path-remap tax.
- Not path-related (product/deps decisions): 2 — #1727 (GPT-5.5 default; rides the no-OpenAI-models decision), #1745 (deepagents 0.7.0a7 alpha validation).
Net: 8 of 19 rows become clean/near-clean, and every future upstream commit stops needing path remaps — the standing tax on the cherry-pick pipeline disappears (recent won't-merge rows #1759/#1769 cited the layout mismatch explicitly).
6. Execution plan sketch (one commit per cascade class; validate ruff → pytest --co → unit → e2e per layer)
Work on a throwaway branch off dev; promote as one PR with granular commits.
- C1 — docs/resources/assets (~1h):
git mvINSTALLATION.md/CUSTOMIZATION.md→docs/,static/→assets/(check README badge/image refs),default_prompt.md→agent/resources/+ apply theprompt.pyimportlib.resources hunk +agent/resources/__init__.py; build wheel, confirm the .md ships. Gate: ruff + unit. - C2 —
agent/review/package (~half day): mv 9 reviewer/style modules, rewrite the 38 importer files, addagent/review/__init__.py(fork's export surface). Gate: ruff +pytest --co+ reviewer unit tests. - C3 — graphs/runtime/providers shims +
langgraph.json(~2h): take upstream's 21 structural A-files nearly verbatim (they delegate toagent.serveretc., which is fork code), add fork-onlyagent/graphs/ci_monitor.py, retargetlanggraph.json. Gate:pytest --co+make devboots all graphs. Adam sign-off: decision 2. - C4 — FastAPI split (~1–1.5 days, the critical commit): split fork's
webapp.py→webhooks/common.py+api/{app,health}.py+{github,linear,slack,jira,confluence}_routes.py(+ Connect routes per decision 6); sed handlerswebapp.→common.; retarget 240 test monkeypatch sites +tests/conftest.py+tests/e2e/harness.py; keepwebapp.pyshim. Gate: full unit + E2E (Playwright + real LangGraph dev server) — the only layer that catches wiring drift. Adam sign-offs: decisions 1, 4, 6; /sh-security-review on this commit. - C5 — tests// moves (~half day): mechanical map moves (§2a) + R<100 monkeypatch retargets not already done in C4 + the 13 fork-only placements (§2c). Gate:
pytest --co+ full unit (paths only, no logic). - C6 — UI features/ moves (~half day–1 day):
git mvper map incl.ported/→experiments|chat, rewrite@/imports in the 54 importer files + moved files, tsconfig/eslint exclude glob, AgentPromptBar shim retarget, deleteui/pnpm-workspace.yaml(lockfile hygiene). Gate:bunx tsc --noEmit+ bun build + Playwright e2e. Adam sign-off: decision 5. - C7 — docs + ledger + memory (~2h): update fork
CLAUDE.md/README/AGENTS.mdpath references, flip8356eb34to Landed intriage.jsonl(+ re-triage notes on the 8 unblocked rows), update the open-swe project memory, Confluence architecture page if module map is documented there.
Effort estimate: 3–5 focused days end-to-end, dominated by C4. Rollback story is clean: each commit is independently revertable until C4; after C4, C5/C6 are path-only.
Spot-checks validating the classification method
- Mechanical —
tests/test_dispatch.py → tests/agent/test_dispatch.pylistedR100in the diff-tree output (byte-identical move); fork's diverged copy moves viagit mvuntouched. - Hand-carry (R<100) —
agent/reviewer_publish.py → agent/review/publish.py(R099): upstream's entire edit is the import block (from .reviewer_findings import→from .findings import,from .utils.github_http import→from ..utils.github_http import); no logic lines. Worst-scoring testtests/test_pr_ready_auto_review.py(R062): every hunk ismonkeypatch.setattr(webapp, ...)→monkeypatch.setattr(webhook_common, ...)/webapp.process_github_pr_ready→github_webhooks.process_github_pr_ready. Worst-scoring fork-diverged UI fileAgentThreadView.tsx(R092): grep of the rename diff shows 0 non-import changed lines. - Consolidation — post-reorg
agent/webapp.pyis exactlyfrom .api.app import app(4 lines, verified viagit show 8356eb34:agent/webapp.py);agent/webhooks/github.py's reorg diff swapsfrom agent import webappforfrom . import commonand rewriteswebapp.X→common.Xwith the docstring explicitly noting module-attribute access is preserved for monkeypatching. - Test pairing —
tests/conftest.pyreorg diff retargets the autouse auto-review fixture fromagent.webapptoagent.webhooks.common, confirming test fixtures follow the split, not the moves.
Working artifacts: docs/upstream-sync/domain-reorg/{movemap-m50.txt, fork-divergence-status.txt, cross.json, forkonly.json, merge-tree-now.txt, head-tree.txt, upstream-pre-tree.txt, upstream-main-tree.txt}.