diff --git a/cmd/joj3/main.go b/cmd/joj3/main.go
index eef373f..39016fd 100644
--- a/cmd/joj3/main.go
+++ b/cmd/joj3/main.go
@@ -11,8 +11,7 @@ import (
 
 	joj3Conf "github.com/joint-online-judge/JOJ3/cmd/joj3/conf"
 	"github.com/joint-online-judge/JOJ3/cmd/joj3/env"
-	"github.com/joint-online-judge/JOJ3/cmd/joj3/stage"
-	internalStage "github.com/joint-online-judge/JOJ3/internal/stage"
+	"github.com/joint-online-judge/JOJ3/internal/stage"
 )
 
 var (
@@ -92,11 +91,11 @@ func mainImpl() (err error) {
 	groups := joj3Conf.MatchGroups(conf, conventionalCommit)
 	env.Attr.Groups = strings.Join(groups, ",")
 	env.Set()
-	_, forceQuitStageName, err := stage.Run(
+	_, forceQuitStageName, err := runStages(
 		conf,
 		groups,
 		func(
-			stageResults []internalStage.StageResult,
+			stageResults []stage.StageResult,
 			forceQuitStageName string,
 		) {
 			env.Attr.ForceQuitStageName = forceQuitStageName
diff --git a/cmd/joj3/stage/run.go b/cmd/joj3/stage.go
similarity index 91%
rename from cmd/joj3/stage/run.go
rename to cmd/joj3/stage.go
index e9d4fff..1357f04 100644
--- a/cmd/joj3/stage/run.go
+++ b/cmd/joj3/stage.go
@@ -1,8 +1,10 @@
-package stage
+package main
 
 import (
+	"encoding/json"
 	"fmt"
 	"log/slog"
+	"os"
 	"strings"
 
 	"github.com/joint-online-judge/JOJ3/cmd/joj3/conf"
@@ -132,7 +134,7 @@ func newErrorStageResults(err error) ([]stage.StageResult, string) {
 	}, "Internal Error"
 }
 
-func Run(
+func runStages(
 	conf *conf.Conf,
 	groups []string,
 	onStagesComplete func([]stage.StageResult, string),
@@ -175,8 +177,16 @@ func Run(
 		stageResults, forceQuitStageName = newErrorStageResults(err)
 	}
 	onStagesComplete(stageResults, forceQuitStageName)
-	slog.Info("write stageResults")
-	if err = Write(conf.Stage.OutputPath, stageResults); err != nil {
+	slog.Info("output result start", "path", conf.Stage.OutputPath)
+	slog.Debug("output result start",
+		"path", conf.Stage.OutputPath, "results", stageResults)
+	content, err := json.Marshal(stageResults)
+	if err != nil {
+		slog.Error("marshal stageResults", "error", err)
+	}
+	err = os.WriteFile(conf.Stage.OutputPath,
+		append(content, []byte("\n")...), 0o600)
+	if err != nil {
 		slog.Error("write stageResults", "error", err)
 	}
 	slog.Info("run postStages")
diff --git a/cmd/joj3/stage/write.go b/cmd/joj3/stage/write.go
deleted file mode 100644
index cb1b357..0000000
--- a/cmd/joj3/stage/write.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package stage
-
-import (
-	"encoding/json"
-	"log/slog"
-	"os"
-
-	"github.com/joint-online-judge/JOJ3/internal/stage"
-)
-
-func Write(outputPath string, results []stage.StageResult) error {
-	slog.Info("output result start", "path", outputPath)
-	slog.Debug("output result start", "path", outputPath, "results", results)
-	content, err := json.Marshal(results)
-	if err != nil {
-		return err
-	}
-	return os.WriteFile(outputPath,
-		append(content, []byte("\n")...), 0o600)
-}