fix: slackpath trees use batch endpoint for atomic multi-component adds

Updates slackpath-2 and slackpath-4 to use POST /projects/{id}/components/batch
for adding multiple Go components atomically in a single git commit. This
prevents the go.work race condition where individual commits reference modules
that don't exist yet.

Also adds on_error: continue for infrastructure provisioning steps that may
already exist from skeleton (redis, postgres).

Verified:
- slackpath-1:  Complete (wait_build polled 5 times, detected success)
- slackpath-2:  Complete (wait_build polled 111 times, detected success)
- slackpath-3:  Infrastructure passed (worker capacity limited testing)
- slackpath-4:  Infrastructure passed (worker capacity limited testing)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
jordan 2026-02-05 14:44:53 -07:00
parent da482b48b4
commit 3b0779fbe8
6 changed files with 42 additions and 57 deletions

View File

@ -30,28 +30,21 @@ steps:
type: redis type: redis
name: "job-queue" name: "job-queue"
add-api: add-components:
description: Public API (Producer) description: Add API + Worker atomically (single git commit)
depends_on: [create-project, add-redis] depends_on: [create-project, add-redis]
action: api action: api
method: POST method: POST
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" endpoint: "/projects/{{ .outputs.create-project.project_id }}/components/batch"
body: body:
type: service components:
- type: service
name: "api" name: "api"
- type: worker
add-worker:
description: Worker Service (Consumer)
depends_on: [create-project, add-redis]
action: api
method: POST
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components"
body:
type: worker
name: "background-processor" name: "background-processor"
wait-infra: wait-infra:
depends_on: [create-project, add-api, add-worker] depends_on: [create-project, add-components]
action: wait_pipeline action: wait_pipeline
project_id: "{{ .outputs.create-project.project_id }}" project_id: "{{ .outputs.create-project.project_id }}"

View File

@ -20,8 +20,9 @@ steps:
- domain: .data.domain - domain: .data.domain
add-redis: add-redis:
description: Add Redis for Pub/Sub description: Add Redis for Pub/Sub (may already exist from skeleton)
depends_on: [create-project] depends_on: [create-project]
on_error: continue
action: api action: api
method: POST method: POST
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" endpoint: "/projects/{{ .outputs.create-project.project_id }}/components"

View File

@ -20,8 +20,9 @@ steps:
- domain: .data.domain - domain: .data.domain
add-db: add-db:
description: Add CockroachDB for user/auth storage description: Add CockroachDB for user/auth storage (may already exist from skeleton)
depends_on: [create-project] depends_on: [create-project]
on_error: continue
action: api action: api
method: POST method: POST
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" endpoint: "/projects/{{ .outputs.create-project.project_id }}/components"
@ -30,8 +31,9 @@ steps:
name: "main-db" name: "main-db"
add-redis: add-redis:
description: Add Redis for job queue and pub/sub description: Add Redis for job queue and pub/sub (may already exist from skeleton)
depends_on: [create-project] depends_on: [create-project]
on_error: continue
action: api action: api
method: POST method: POST
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" endpoint: "/projects/{{ .outputs.create-project.project_id }}/components"
@ -39,29 +41,23 @@ steps:
type: redis type: redis
name: "job-queue" name: "job-queue"
add-auth: add-components:
depends_on: [add-db] description: Add auth, chat, and worker atomically (single git commit)
depends_on: [add-db, add-redis]
action: api action: api
method: POST method: POST
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" endpoint: "/projects/{{ .outputs.create-project.project_id }}/components/batch"
body: { type: service, name: "auth-svc" } body:
components:
add-chat: - type: service
depends_on: [add-redis] name: "auth-svc"
action: api - type: service
method: POST name: "chat-svc"
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" - type: worker
body: { type: service, name: "chat-svc" } name: "worker-svc"
add-worker:
depends_on: [add-redis]
action: api
method: POST
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components"
body: { type: worker, name: "worker-svc" }
wait-infra: wait-infra:
depends_on: [add-auth, add-chat, add-worker] depends_on: [add-components]
action: wait_pipeline action: wait_pipeline
project_id: "{{ .outputs.create-project.project_id }}" project_id: "{{ .outputs.create-project.project_id }}"

View File

@ -3,21 +3,19 @@ FROM golang:1.23-alpine AS builder
RUN apk add --no-cache git RUN apk add --no-cache git
# Configure Go workspace and private modules # Configure Go private modules
# Disable workspace mode - each component builds independently with replace directives
ENV GOPRIVATE=git.threesix.ai/* ENV GOPRIVATE=git.threesix.ai/*
ENV GOWORK=/app/go.work ENV GOWORK=off
WORKDIR /app WORKDIR /app
# Copy go workspace and all source (workspace deps are local) # Copy shared pkg and this service only
# Note: go.work.sum may not exist if no external dependencies have been synced yet
COPY go.work ./
COPY go.work.su[m] ./
COPY pkg/ ./pkg/ COPY pkg/ ./pkg/
COPY services/{{COMPONENT_NAME}}/ ./services/{{COMPONENT_NAME}}/ COPY services/{{COMPONENT_NAME}}/ ./services/{{COMPONENT_NAME}}/
# Build from workspace root # Build from the service directory (uses replace directive for ../pkg)
RUN CGO_ENABLED=0 go build -o /{{COMPONENT_NAME}} ./services/{{COMPONENT_NAME}}/cmd/server RUN cd services/{{COMPONENT_NAME}} && CGO_ENABLED=0 go build -o /{{COMPONENT_NAME}} ./cmd/server
# Production stage # Production stage
FROM alpine:3.19 FROM alpine:3.19

View File

@ -3,21 +3,19 @@ FROM golang:1.23-alpine AS builder
RUN apk add --no-cache git RUN apk add --no-cache git
# Configure Go workspace and private modules # Configure Go private modules
# Disable workspace mode - each component builds independently with replace directives
ENV GOPRIVATE=git.threesix.ai/* ENV GOPRIVATE=git.threesix.ai/*
ENV GOWORK=/app/go.work ENV GOWORK=off
WORKDIR /app WORKDIR /app
# Copy go workspace and all source (workspace deps are local) # Copy shared pkg and this worker only
# Note: go.work.sum may not exist if no external dependencies have been synced yet
COPY go.work ./
COPY go.work.su[m] ./
COPY pkg/ ./pkg/ COPY pkg/ ./pkg/
COPY workers/{{COMPONENT_NAME}}/ ./workers/{{COMPONENT_NAME}}/ COPY workers/{{COMPONENT_NAME}}/ ./workers/{{COMPONENT_NAME}}/
# Build from workspace root # Build from the worker directory (uses replace directive for ../pkg)
RUN CGO_ENABLED=0 go build -o /{{COMPONENT_NAME}} ./workers/{{COMPONENT_NAME}}/cmd/worker RUN cd workers/{{COMPONENT_NAME}} && CGO_ENABLED=0 go build -o /{{COMPONENT_NAME}} ./cmd/worker
# Production stage # Production stage
FROM alpine:3.19 FROM alpine:3.19

View File

@ -9,12 +9,11 @@ import (
"syscall" "syscall"
"time" "time"
"{{GO_MODULE}}/pkg/config"
"{{GO_MODULE}}/pkg/database" "{{GO_MODULE}}/pkg/database"
"{{GO_MODULE}}/pkg/logging" "{{GO_MODULE}}/pkg/logging"
"{{GO_MODULE}}/pkg/queue" "{{GO_MODULE}}/pkg/queue"
"{{GO_MODULE}}/workers/{{COMPONENT_NAME}}/internal/config"
"{{GO_MODULE}}/workers/{{COMPONENT_NAME}}/internal/handlers" "{{GO_MODULE}}/workers/{{COMPONENT_NAME}}/internal/handlers"
workerconfig "{{GO_MODULE}}/workers/{{COMPONENT_NAME}}/internal/config"
) )
//go:embed migrations/*.sql //go:embed migrations/*.sql
@ -28,7 +27,7 @@ func main() {
}).WithService("{{COMPONENT_NAME}}") }).WithService("{{COMPONENT_NAME}}")
// Initialize configuration // Initialize configuration
cfg, err := workerconfig.Load() cfg, err := config.Load()
if err != nil { if err != nil {
logger.Error("failed to load config", "error", err) logger.Error("failed to load config", "error", err)
os.Exit(1) os.Exit(1)