29 KiB
codespace Backend Skeleton Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Build a runnable Go backend MVP in the current project root that manages workspaces, file operations through an abstract filesystem, and OpenCode processes.
Architecture: HTTP handlers decode requests and delegate to service objects. Services coordinate workspace.Manager, fs.FileSystem, and process.Manager. Local filesystem and OpenCode process execution are implementation details hidden behind focused interfaces.
Tech Stack: Go 1.22+, standard library HTTP router, YAML config via gopkg.in/yaml.v3, local filesystem, OS processes.
Global Constraints
- Current directory
/Users/taochen/llm/codespaceis the project root; do not create a nestedcodespace/directory. - First version is a runnable backend MVP, not a complete IDE.
- Do not add Docker.
- Do not implement authentication, Git integration, LSP, AI Agent orchestration, web frontend, or full watcher/WebSocket event streaming.
- Handlers must not call
os.*file APIs orexec.*; file and process work goes through services. - File API paths are workspace-relative; never expose host absolute paths in file responses.
- Workspace IDs allow only letters, digits,
-,_, and.. - Prevent
../and absolute-path escape from workspace roots. - Process command defaults to
opencode, but must be configurable. - Current directory is not a git repository, so this plan does not include commit steps unless the user initializes git separately.
- Code generation/refactoring must be delegated to Codex MCP using exactly
model: "kimi/kimi-k2.7-code",sandbox: "danger-full-access", andapproval-policy: "on-failure".
File Structure
Create
go.mod— module declaration and dependencies.Makefile— local development commands.README.md— project overview, API examples, run/test instructions.configs/config.yaml— default runtime config.cmd/server/main.go— app entrypoint, config load, dependency wiring, HTTP server start.pkg/config/config.go— config structs and load logic.pkg/response/response.go— consistent JSON success/error responses.pkg/logger/logger.go— tiny standard-library logger wrapper.internal/util/path.go— reusable path validation helpers.internal/util/errors.go— typed app errors and helpers.internal/workspace/workspace.go—Workspacemodel.internal/workspace/path.go— workspace ID validation and root path construction.internal/workspace/manager.go— local workspace manager implementation.internal/workspace/path_test.go— workspace ID/path tests.internal/fs/fileinfo.go— file metadata model.internal/fs/filesystem.go—FileSysteminterface.internal/fs/localfs.go— local filesystem implementation bound to one workspace root.internal/fs/localfs_test.go— path escape and file operation tests.internal/process/session.go— session/status structs.internal/process/manager.go— process manager interface and implementation.internal/process/opencode.go— OpenCode command/session creation details.internal/process/manager_test.go— status/start/stop tests with safe test command.internal/model/workspace.go— workspace API request/response structs.internal/model/file.go— file API request/response structs.internal/model/process.go— process API response structs.internal/service/workspace_service.go— workspace use cases.internal/service/file_service.go— file use cases.internal/service/process_service.go— process use cases.internal/api/router.go— HTTP router and route registration.internal/api/workspace_handler.go— workspace endpoints.internal/api/file_handler.go— file endpoints.internal/api/process_handler.go— process endpoints.internal/api/router_test.go— health/workspace/file/process route smoke tests.internal/watcher/watcher.go— narrow watcher interface placeholder.web/.gitkeep— keeps frontend placeholder directory.docs/superpowers/specs/2026-07-02-codespace-design.md— already created design spec.docs/superpowers/plans/2026-07-02-codespace-backend-skeleton.md— this plan.
External dependency
Only one non-stdlib dependency is allowed for the first version:
gopkg.in/yaml.v3
Everything else must use the Go standard library.
Task 1: Initialize Go module, config, responses, and docs
Files:
- Create:
go.mod - Create:
Makefile - Create:
README.md - Create:
configs/config.yaml - Create:
pkg/config/config.go - Create:
pkg/response/response.go - Create:
pkg/logger/logger.go - Create:
web/.gitkeep
Interfaces:
-
Produces:
config.Config,config.Load(path string) (Config, error). -
Produces:
response.JSON(w http.ResponseWriter, status int, value any). -
Produces:
response.Error(w http.ResponseWriter, status int, code string, err error). -
Produces:
logger.New() *log.Logger. -
Step 1: Create module and default config
Create go.mod:
module codespace
go 1.22
require gopkg.in/yaml.v3 v3.0.1
Create configs/config.yaml:
server:
addr: ":8080"
workspace:
root: "./workspaces"
process:
opencodeCommand: "opencode"
Create web/.gitkeep as an empty file.
- Step 2: Implement config loader
Create pkg/config/config.go with these exported names:
package config
type Config struct {
Server ServerConfig `yaml:"server"`
Workspace WorkspaceConfig `yaml:"workspace"`
Process ProcessConfig `yaml:"process"`
}
type ServerConfig struct {
Addr string `yaml:"addr"`
}
type WorkspaceConfig struct {
Root string `yaml:"root"`
}
type ProcessConfig struct {
OpenCodeCommand string `yaml:"opencodeCommand"`
}
func Default() Config
func Load(path string) (Config, error)
Required behavior:
-
Default()returns:8080,./workspaces, andopencode. -
Load(path)starts from defaults. -
If
pathexists, YAML values override defaults. -
If
pathdoes not exist, return defaults and no error. -
Environment variables override both defaults and file values:
CODESPACE_ADDRCODESPACE_WORKSPACE_ROOTCODESPACE_OPENCODE_COMMAND
-
Step 3: Implement response helpers
Create pkg/response/response.go with this response shape:
type ErrorBody struct {
Error ErrorDetail `json:"error"`
}
type ErrorDetail struct {
Code string `json:"code"`
Message string `json:"message"`
}
Functions:
func JSON(w http.ResponseWriter, status int, value any)
func Error(w http.ResponseWriter, status int, code string, err error)
Required behavior:
-
Always set
Content-Type: application/json. -
JSONwrites the status code and JSON-encodesvalue. -
Errorwrites{"error":{"code":"...","message":"..."}}. -
Step 4: Implement logger helper
Create pkg/logger/logger.go:
package logger
func New() *log.Logger
Required behavior:
-
Use standard library
log.New(os.Stdout, "codespace ", log.LstdFlags|log.Lshortfile). -
Step 5: Create README and Makefile
Create Makefile with these targets:
.PHONY: test run fmt
test:
go test ./...
run:
go run ./cmd/server
fmt:
gofmt -w cmd internal pkg
Create README.md with:
-
Project purpose.
-
First-version scope.
-
make testandmake runinstructions. -
Example curl for
POST /api/workspacesandGET /healthz. -
Explicit note that Docker/auth/Git/LSP/frontend are not implemented in v1.
-
Step 6: Validate task
Run:
go mod tidy
go test ./...
Expected:
go mod tidysucceeds.go test ./...succeeds or reports no test files for created packages.
Task 2: Implement typed errors and workspace manager
Files:
- Create:
internal/util/errors.go - Create:
internal/util/path.go - Create:
internal/workspace/workspace.go - Create:
internal/workspace/path.go - Create:
internal/workspace/manager.go - Create:
internal/workspace/path_test.go
Interfaces:
-
Produces:
util.AppError,util.Code,util.New(code Code, message string) error. -
Produces:
workspace.Workspace. -
Produces:
workspace.Managerinterface. -
Produces:
workspace.LocalManagerimplementation. -
Produces:
workspace.ValidID(id string) bool. -
Step 1: Write workspace path tests first
Create internal/workspace/path_test.go with tests covering:
func TestValidIDAcceptsSafeSegments(t *testing.T)
func TestValidIDRejectsUnsafeSegments(t *testing.T)
func TestRootForUsesConfiguredRoot(t *testing.T)
Cases:
-
valid:
user1,project-A,project_A,project.1 -
invalid: empty string,
/tmp/x,../x,a/b,a b,中文,* -
RootFor("/tmp/workspaces", "user1")returns/tmp/workspaces/user1after filepath cleaning. -
Step 2: Implement typed errors
Create internal/util/errors.go with codes:
type Code string
const (
CodeBadRequest Code = "bad_request"
CodeNotFound Code = "not_found"
CodeConflict Code = "conflict"
CodeInternal Code = "internal"
)
type AppError struct {
Code Code
Message string
Err error
}
func (e *AppError) Error() string
func (e *AppError) Unwrap() error
func New(code Code, message string) error
func Wrap(code Code, message string, err error) error
func CodeOf(err error) Code
Required behavior:
-
CodeOf(nil)returnsCodeInternal. -
CodeOf(err)returns embeddedAppError.Codeif present. -
Non-
AppErrordefaults toCodeInternal. -
Step 3: Implement path helper
Create internal/util/path.go with:
func CleanRelative(path string) (string, error)
Required behavior:
-
Empty path and
.return.. -
Absolute paths are rejected with bad request.
-
Paths that clean to
..or start with../are rejected with bad request. -
Backslash should be normalized by filepath semantics on the current platform; do not implement Windows-specific policy in v1.
-
Step 4: Implement workspace model and manager
Create internal/workspace/workspace.go:
type Workspace struct {
ID string `json:"id"`
Root string `json:"root"`
}
Create internal/workspace/path.go:
func ValidID(id string) bool
func RootFor(baseRoot, id string) string
Create internal/workspace/manager.go:
type Manager interface {
Create(id string) (*Workspace, error)
Get(id string) (*Workspace, error)
Delete(id string) error
}
type LocalManager struct {
root string
}
func NewLocalManager(root string) *LocalManager
Required behavior:
-
NewLocalManagerstores an absolute, cleaned root if possible. -
Createvalidates ID, creates the directory with0755, and returns workspace. -
Getvalidates ID and returns not found if directory does not exist. -
Deletevalidates ID and removes the workspace directory recursively. -
Manager must never accept unsafe IDs.
-
Step 5: Run focused tests
Run:
go test ./internal/workspace ./internal/util
Expected:
- All tests pass.
Task 3: Implement FileSystem interface and LocalFS
Files:
- Create:
internal/fs/fileinfo.go - Create:
internal/fs/filesystem.go - Create:
internal/fs/localfs.go - Create:
internal/fs/localfs_test.go
Interfaces:
-
Produces:
fs.FileInfo. -
Produces:
fs.FileSystem. -
Produces:
fs.NewLocal(root string) FileSystem. -
Step 1: Write LocalFS tests first
Create internal/fs/localfs_test.go with tests:
func TestLocalFSWriteReadListStat(t *testing.T)
func TestLocalFSRejectsPathEscape(t *testing.T)
func TestLocalFSRenameAndRemove(t *testing.T)
Required assertions:
-
Write
dir/main.gowith contentpackage main\n. -
Read returns exact bytes.
-
List
dirreturns one item namedmain.gowith pathdir/main.goandIsDir == false. -
Stat
dir/main.goreturns size equal to content length. -
Read
../outside, Write../outside, Mkdir../outside, Remove../outside, Renamefileto../outsideall fail. -
Rename
old.txttonew.txtmakesold.txtmissing andnew.txtreadable. -
Step 2: Define file models and interface
Create internal/fs/fileinfo.go:
type FileInfo struct {
Name string `json:"name"`
Path string `json:"path"`
IsDir bool `json:"isDir"`
Size int64 `json:"size"`
ModTime time.Time `json:"modTime"`
}
Create internal/fs/filesystem.go:
type FileSystem interface {
List(path string) ([]FileInfo, error)
Read(path string) ([]byte, error)
Write(path string, data []byte) error
Mkdir(path string) error
Remove(path string) error
Rename(oldPath, newPath string) error
Stat(path string) (*FileInfo, error)
}
- Step 3: Implement LocalFS safely
Create internal/fs/localfs.go with:
type LocalFS struct {
root string
}
func NewLocal(root string) *LocalFS
Required behavior:
-
Store root as an absolute cleaned path if possible.
-
A private
resolve(path string) (absolute string, relative string, error)must callutil.CleanRelativeand ensure the final absolute path stays underroot. -
Writecreates parent directories with0755and writes file with0644. -
Mkdircreates directories with0755. -
Listreturns sorted entries by name for deterministic tests. -
Read,List, andStatmap missing files toCodeNotFound. -
Removeremoves files or directories recursively. -
Renamecreates destination parent directories and rejects destination escape. -
Step 4: Run focused tests
Run:
go test ./internal/fs ./internal/util
Expected:
- All tests pass.
Task 4: Implement process manager for OpenCode sessions
Files:
- Create:
internal/process/session.go - Create:
internal/process/manager.go - Create:
internal/process/opencode.go - Create:
internal/process/manager_test.go
Interfaces:
-
Produces:
process.Status. -
Produces:
process.Managerinterface. -
Produces:
process.NewManager(command string) *LocalManager. -
Step 1: Write process manager tests first
Create internal/process/manager_test.go with tests:
func TestStatusBeforeStartIsNotRunning(t *testing.T)
func TestStartRejectsDuplicateRunningSession(t *testing.T)
func TestStopMissingSessionIsNotFound(t *testing.T)
Use a safe long-running command:
cmd := os.Args[0]
args := []string{"-test.run=TestHelperProcess", "--"}
And helper pattern:
func TestHelperProcess(t *testing.T) {
if os.Getenv("CODESPACE_HELPER_PROCESS") != "1" {
return
}
select {}
}
The implementation must allow tests to override command and args, either through a constructor option or an unexported test helper in the same package.
- Step 2: Define session/status structs
Create internal/process/session.go:
type Status struct {
WorkspaceID string `json:"workspaceId"`
Running bool `json:"running"`
PID int `json:"pid,omitempty"`
}
type Session struct {
WorkspaceID string
Root string
Cmd *exec.Cmd
}
- Step 3: Define manager interface and implementation
Create internal/process/manager.go:
type Manager interface {
Start(workspaceID string, workspaceRoot string) error
Stop(workspaceID string) error
Restart(workspaceID string, workspaceRoot string) error
Status(workspaceID string) Status
}
type LocalManager struct {
command string
args []string
mu sync.Mutex
sessions map[string]*Session
}
func NewManager(command string) *LocalManager
Required behavior:
-
Empty command defaults to
opencode. -
Startrejects duplicate running session withCodeConflict. -
Startusesexec.Command(command, args...). -
Startsetscmd.Dir = workspaceRoot. -
StartappendsHOME=<workspaceRoot>to environment. -
Startstarts the process and stores session by workspace ID. -
Stopreturns not found if no running session exists. -
Stopkills the process and removes the session. -
RestartcallsStopif running, thenStart. -
Statusreturns running and PID only if a session exists and process is started. -
Step 4: Implement OpenCode command helper
Create internal/process/opencode.go with small helpers only:
const DefaultOpenCodeCommand = "opencode"
func normalizeCommand(command string) string
- Step 5: Run focused tests
Run:
go test ./internal/process
Expected:
- All tests pass.
- No real
opencodebinary is required for tests.
Task 5: Implement services and API models
Files:
- Create:
internal/model/workspace.go - Create:
internal/model/file.go - Create:
internal/model/process.go - Create:
internal/service/workspace_service.go - Create:
internal/service/file_service.go - Create:
internal/service/process_service.go
Interfaces:
-
Produces: model request/response structs used by handlers.
-
Produces:
service.WorkspaceService. -
Produces:
service.FileService. -
Produces:
service.ProcessService. -
Step 1: Create API models
Create internal/model/workspace.go:
type CreateWorkspaceRequest struct {
ID string `json:"id"`
}
type WorkspaceResponse struct {
ID string `json:"id"`
}
Do not include host root path in API responses.
Create internal/model/file.go:
type WriteFileRequest struct {
Path string `json:"path"`
Content string `json:"content"`
}
type MkdirRequest struct {
Path string `json:"path"`
}
type RenameRequest struct {
OldPath string `json:"oldPath"`
NewPath string `json:"newPath"`
}
type ReadFileResponse struct {
Path string `json:"path"`
Content string `json:"content"`
}
Create internal/model/process.go:
type ProcessStatusResponse struct {
WorkspaceID string `json:"workspaceId"`
Running bool `json:"running"`
PID int `json:"pid,omitempty"`
}
- Step 2: Implement WorkspaceService
Create internal/service/workspace_service.go:
type WorkspaceService struct {
workspaces workspace.Manager
processes process.Manager
}
func NewWorkspaceService(workspaces workspace.Manager, processes process.Manager) *WorkspaceService
func (s *WorkspaceService) Create(id string) (*workspace.Workspace, error)
func (s *WorkspaceService) Get(id string) (*workspace.Workspace, error)
func (s *WorkspaceService) Delete(id string) error
Required behavior:
-
Deletechecks process status and stops it if running before deleting workspace. -
Step 3: Implement FileService
Create internal/service/file_service.go:
type FileService struct {
workspaces workspace.Manager
}
func NewFileService(workspaces workspace.Manager) *FileService
func (s *FileService) List(workspaceID, path string) ([]fs.FileInfo, error)
func (s *FileService) Read(workspaceID, path string) ([]byte, error)
func (s *FileService) Write(workspaceID, path string, data []byte) error
func (s *FileService) Mkdir(workspaceID, path string) error
func (s *FileService) Remove(workspaceID, path string) error
func (s *FileService) Rename(workspaceID, oldPath, newPath string) error
func (s *FileService) Stat(workspaceID, path string) (*fs.FileInfo, error)
Required behavior:
-
Each method calls
workspaces.Get(workspaceID)first. -
Each method constructs
fs.NewLocal(ws.Root)and delegates. -
Step 4: Implement ProcessService
Create internal/service/process_service.go:
type ProcessService struct {
workspaces workspace.Manager
processes process.Manager
}
func NewProcessService(workspaces workspace.Manager, processes process.Manager) *ProcessService
func (s *ProcessService) Start(workspaceID string) error
func (s *ProcessService) Stop(workspaceID string) error
func (s *ProcessService) Restart(workspaceID string) error
func (s *ProcessService) Status(workspaceID string) (process.Status, error)
Required behavior:
-
Start,Restart, andStatusverify workspace exists. -
Stopshould return process manager's result directly; if no process exists, return not found. -
Restartuses workspace root from manager. -
Step 5: Run package tests
Run:
go test ./internal/service ./internal/model ./internal/workspace ./internal/fs ./internal/process
Expected:
- All packages compile and tests pass.
Task 6: Implement HTTP router and handlers
Files:
- Create:
internal/api/router.go - Create:
internal/api/workspace_handler.go - Create:
internal/api/file_handler.go - Create:
internal/api/process_handler.go - Create:
internal/api/router_test.go
Interfaces:
-
Produces:
api.NewRouter(workspaces *service.WorkspaceService, files *service.FileService, processes *service.ProcessService) http.Handler. -
Step 1: Write HTTP smoke tests first
Create internal/api/router_test.go with tests:
func TestHealthz(t *testing.T)
func TestCreateWorkspace(t *testing.T)
func TestWriteReadFile(t *testing.T)
func TestProcessStatus(t *testing.T)
Required assertions:
GET /healthzreturns200and{"status":"ok"}.POST /api/workspaceswith{"id":"user1"}returns201and{"id":"user1"}.PUT /api/workspaces/user1/files/writewith{"path":"main.go","content":"package main\n"}returns204.GET /api/workspaces/user1/files/read?path=main.goreturns content.GET /api/workspaces/user1/process/statusreturns200, workspace ID, andrunning:false.
Test setup must use t.TempDir() for workspace root and a process manager with a harmless command. Do not require real OpenCode.
- Step 2: Implement router
Create internal/api/router.go:
func NewRouter(workspaces *service.WorkspaceService, files *service.FileService, processes *service.ProcessService) http.Handler
Required routes:
GET /healthz
POST /api/workspaces
GET /api/workspaces/{id}
DELETE /api/workspaces/{id}
GET /api/workspaces/{id}/files
GET /api/workspaces/{id}/files/read
PUT /api/workspaces/{id}/files/write
POST /api/workspaces/{id}/files/mkdir
DELETE /api/workspaces/{id}/files
POST /api/workspaces/{id}/files/rename
POST /api/workspaces/{id}/process/start
POST /api/workspaces/{id}/process/stop
POST /api/workspaces/{id}/process/restart
GET /api/workspaces/{id}/process/status
Use only the standard library. Since Go 1.22 http.ServeMux supports method/path patterns, use patterns like:
mux.HandleFunc("GET /healthz", healthHandler)
mux.HandleFunc("POST /api/workspaces", workspaceHandler.create)
mux.HandleFunc("GET /api/workspaces/{id}", workspaceHandler.get)
Use r.PathValue("id") for path variables.
- Step 3: Implement workspace handler
Create internal/api/workspace_handler.go.
Required behavior:
-
Decode JSON for create.
-
Empty or invalid JSON returns
400. -
Create success returns
201withmodel.WorkspaceResponse{ID: ws.ID}. -
Get success returns
200with ID only. -
Delete success returns
204. -
Map
util.CodeBadRequestto HTTP 400,CodeNotFoundto 404,CodeConflictto 409, default to 500. -
Step 4: Implement file handler
Create internal/api/file_handler.go.
Required behavior:
-
Listreads querypath; default.. -
Readreads querypath; missing path returns400. -
Writedecodesmodel.WriteFileRequest; missing path returns400; success returns204. -
Mkdirdecodesmodel.MkdirRequest; missing path returns400; success returns204. -
Removereads querypath; missing path returns400; success returns204. -
Renamedecodesmodel.RenameRequest; missing old or new path returns400; success returns204. -
File read returns
model.ReadFileResponsewith content as UTF-8 string. -
Step 5: Implement process handler
Create internal/api/process_handler.go.
Required behavior:
-
Start/stop/restart return
204on success. -
Status returns
model.ProcessStatusResponse. -
Error mapping matches workspace handler.
-
Step 6: Run HTTP tests
Run:
go test ./internal/api
Expected:
- All tests pass.
Task 7: Wire server entrypoint and watcher placeholder
Files:
- Create:
cmd/server/main.go - Create:
internal/watcher/watcher.go
Interfaces:
-
Produces runnable server with
go run ./cmd/server. -
Produces placeholder
watcher.Watcherinterface for future expansion. -
Step 1: Implement watcher placeholder
Create internal/watcher/watcher.go:
type Event struct {
Type string `json:"type"`
Path string `json:"path"`
}
type Watcher interface {
Start(root string) error
Stop() error
Events() <-chan Event
}
Do not wire this into API in v1.
- Step 2: Implement server main
Create cmd/server/main.go.
Required behavior:
- Load config from
configs/config.yamlwithconfig.Load. - Create logger with
logger.New(). - Create workspace manager with configured root.
- Create process manager with configured OpenCode command.
- Create services.
- Create router.
- Start HTTP server at configured address.
- Log address and workspace root.
Main dependency flow:
cfg, err := config.Load("configs/config.yaml")
workspaces := workspace.NewLocalManager(cfg.Workspace.Root)
processes := process.NewManager(cfg.Process.OpenCodeCommand)
workspaceSvc := service.NewWorkspaceService(workspaces, processes)
fileSvc := service.NewFileService(workspaces)
processSvc := service.NewProcessService(workspaces, processes)
handler := api.NewRouter(workspaceSvc, fileSvc, processSvc)
server := &http.Server{Addr: cfg.Server.Addr, Handler: handler}
- Step 3: Verify server starts
Run:
go run ./cmd/server
Expected:
- Server logs that it is listening on
:8080. - Stop it manually after confirming startup.
In another shell, if needed:
curl -s http://localhost:8080/healthz
Expected response:
{"status":"ok"}
Task 8: Final validation and cleanup
Files:
- Modify only files created by Tasks 1-7 if validation exposes issues.
Interfaces:
-
Produces fully validated backend skeleton.
-
Step 1: Format all Go code
Run:
gofmt -w cmd internal pkg
Expected:
-
Command exits successfully.
-
Step 2: Tidy modules
Run:
go mod tidy
Expected:
-
go.sumis created or updated if required bygopkg.in/yaml.v3. -
Step 3: Run all tests
Run:
go test ./...
Expected:
-
All tests pass.
-
Step 4: Check handler boundary rule
Run:
grep -R "os\.\|exec\." internal/api || true
Expected:
- No matches.
If matches exist, move those calls out of internal/api into services or lower-level packages.
- Step 5: Run server smoke test
Run server:
go run ./cmd/server
In another terminal:
curl -s http://localhost:8080/healthz
curl -s -X POST http://localhost:8080/api/workspaces \
-H 'Content-Type: application/json' \
-d '{"id":"user1"}'
curl -s -X PUT http://localhost:8080/api/workspaces/user1/files/write \
-H 'Content-Type: application/json' \
-d '{"path":"main.go","content":"package main\n"}'
curl -s 'http://localhost:8080/api/workspaces/user1/files/read?path=main.go'
Expected:
-
Health returns
{"status":"ok"}. -
Workspace create returns
{"id":"user1"}. -
Write returns no body with HTTP 204.
-
Read returns
{"path":"main.go","content":"package main\n"}. -
Step 6: Document final result
Update README.md only if the actual commands or routes differ from the planned ones. Do not add future-roadmap promises beyond the explicit v1 non-goals.
Codex Execution Prompt
Use this exact structure when dispatching Codex for implementation:
## Context
- Project root: /Users/taochen/llm/codespace
- Tech Stack: Go 1.22+, standard library HTTP router, gopkg.in/yaml.v3 for config.
- Design spec: docs/superpowers/specs/2026-07-02-codespace-design.md
- Implementation plan: docs/superpowers/plans/2026-07-02-codespace-backend-skeleton.md
- Current directory is the project root. Do not create a nested codespace directory.
## Task
Generate the runnable Go backend MVP skeleton described by the implementation plan.
Steps:
1. Create the files listed in Tasks 1-7.
2. Implement tests before or alongside implementation for workspace path validation, LocalFS, process manager, and HTTP routes.
3. Run formatting, module tidy, tests, and boundary checks from Task 8.
## Constraints
- Do not add Docker, auth, Git, LSP, AI Agent orchestration, frontend, or full watcher/WebSocket streaming.
- Do not expose host absolute paths in API responses.
- Handlers must not call os.* file APIs or exec.*; use services.
- Prevent workspace path escape and unsafe workspace IDs.
- Use only standard library plus gopkg.in/yaml.v3.
- If an existing file has meaningful content, stop and report before overwriting it.
## Acceptance
- gofmt -w cmd internal pkg succeeds.
- go mod tidy succeeds.
- go test ./... passes.
- grep -R "os\.\|exec\." internal/api || true returns no matches.
- go run ./cmd/server starts and /healthz returns {"status":"ok"}.
Self-Review
- Spec coverage: The plan covers project root handling, runnable backend, workspace manager, FileSystem abstraction, LocalFS, process manager, services, API handlers, config, docs, tests, and watcher placeholder. Non-goals are explicitly excluded.
- Placeholder scan: No
TBD,TODO, or undefined future implementation steps remain. - Type consistency: Function names and signatures are consistent across file structure, task interfaces, and wiring steps.
- Scope check: The plan is one cohesive MVP backend skeleton. Git, LSP, AI Agent, frontend, and watcher streaming remain outside v1 as required.