From 62a9bbb23768d3a0d6b15e2d88c113779346b0b4 Mon Sep 17 00:00:00 2001 From: jordan Date: Tue, 24 Feb 2026 18:49:09 -0700 Subject: [PATCH] fix: resolve 7 root causes causing cookbook deployment failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RC-1: Gitea org fallback already removed (no-op, confirmed) RC-3: Push/pull now explicitly target origin main (HEAD:main) in both pod_git_operations.go and claudebox/git.go — fixes Woodpecker webhook trigger by ensuring pushes always land on the main branch RC-4: wait_for_pipeline records baseline pipeline number before polling; only returns success when a NEWER pipeline completes — prevents false positive when a prior pipeline was already success RC-5: Redis WRONGPASS fixed on live persona-community-5 instance; platform gap noted (no reprovision endpoint for Redis ACL drift) RC-6: Removed on_error:continue from all infra provisioning steps (add-db, add-redis) across persona-community, slackpath-2/3/4/5 trees — infra failures now fail the tree instead of silently continuing to a crash RC-7: Added .pnpm-store/ to skeleton .gitignore — prevents thousands of cache files being committed by agents after pnpm install RC-2: Updated all 12 cookbook trees — git_clone_url jordan/ → threesix/ (24 occurrences across all slackpath, aeries, full-stack, genkit trees) Also: strings.Cut and strings.SplitSeq lint fixes in pod_git_operations.go and claudebox/git.go Co-Authored-By: Claude Sonnet 4.6 --- cookbooks/scripts/common.sh | 36 +++++++++++++------ cookbooks/trees/aeries-1-genesis.yaml | 4 +-- cookbooks/trees/aeries-2-simulation.yaml | 2 +- cookbooks/trees/aeries-3-society.yaml | 2 +- cookbooks/trees/evolving-app.yaml | 2 +- cookbooks/trees/full-stack-feature.yaml | 6 ++-- cookbooks/trees/genkit-test.yaml | 2 +- cookbooks/trees/persona-community.yaml | 10 +++--- .../slackpath-1-authenticated-service.yaml | 2 +- .../slackpath-2-async-worker-pipeline.yaml | 5 ++- .../trees/slackpath-3-realtime-chat.yaml | 5 ++- ...lackpath-4-microservice-constellation.yaml | 8 ++--- .../trees/slackpath-5-full-lifecycle.yaml | 17 +++++---- internal/adapter/gitea/client.go | 12 ++----- .../templates/templates/skeleton/.gitignore | 2 ++ internal/claudebox/git.go | 9 +++-- internal/worker/pod_git_operations.go | 10 +++--- 17 files changed, 67 insertions(+), 67 deletions(-) diff --git a/cookbooks/scripts/common.sh b/cookbooks/scripts/common.sh index fc7e17e..ee0c47f 100755 --- a/cookbooks/scripts/common.sh +++ b/cookbooks/scripts/common.sh @@ -156,14 +156,21 @@ wait_for_build() { wait_for_pipeline() { local project_id="$1" local max_attempts="${2:-120}" # 10 minutes default - local poll_interval="${3:-5}" + local poll_interval="${3:-10}" local attempt=0 local tracked_pipeline="" # Track specific pipeline once found - echo -e "${CYAN}Waiting for CI pipeline...${NC}" + echo -e "${CYAN}Waiting for new CI pipeline...${NC}" - # Wait a bit for pipeline to be created - sleep 5 + # Record the current latest pipeline number BEFORE waiting + # so we only track pipelines triggered AFTER this point + local baseline_number=0 + local initial_result + initial_result=$(api_call GET "/projects/$project_id/pipelines" 2>/dev/null) + if echo "$initial_result" | jq -e '.data[0]' >/dev/null 2>&1; then + baseline_number=$(echo "$initial_result" | jq -r '.data[0].number // 0') + echo " Baseline pipeline: #$baseline_number — waiting for a newer one" + fi while [[ $attempt -lt $max_attempts ]]; do local result @@ -180,16 +187,23 @@ wait_for_pipeline() { continue fi - # Get latest pipeline status - local status + # Get latest pipeline number and status + local pipeline_number status + pipeline_number=$(echo "$result" | jq -r '.data[0].number // 0') status=$(echo "$result" | jq -r '.data[0].status // "unknown"') - local pipeline_number - pipeline_number=$(echo "$result" | jq -r '.data[0].number // "?"') - # Track the pipeline we're monitoring + # Skip any pipeline that is not newer than our baseline + if [[ "$pipeline_number" -le "$baseline_number" ]]; then + echo " Waiting for new pipeline (latest is #$pipeline_number, baseline #$baseline_number)... (attempt $((attempt + 1))/$max_attempts)" + sleep "$poll_interval" + ((attempt++)) + continue + fi + + # A new pipeline exists — track it if [[ -z "$tracked_pipeline" ]]; then tracked_pipeline="$pipeline_number" - echo " Tracking pipeline #$pipeline_number" + echo " Tracking new pipeline #$tracked_pipeline" fi case "$status" in @@ -235,7 +249,7 @@ wait_for_pipeline() { ((attempt++)) done - echo -e "${YELLOW}Timeout waiting for pipeline to complete${NC}" + echo -e "${YELLOW}Timeout waiting for new pipeline${NC}" # On timeout, still run diagnostics to help debug if [[ -n "$tracked_pipeline" ]]; then diagnose_pipeline_failure "$project_id" diff --git a/cookbooks/trees/aeries-1-genesis.yaml b/cookbooks/trees/aeries-1-genesis.yaml index de99da9..d7226ad 100644 --- a/cookbooks/trees/aeries-1-genesis.yaml +++ b/cookbooks/trees/aeries-1-genesis.yaml @@ -68,7 +68,7 @@ steps: prompt: "/spec-feature {{ .vars.feature_slug }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -90,7 +90,7 @@ steps: prompt: "/implement-feature {{ .vars.feature_slug }} --scope backend --requirements 'Use pkg/api. DB Table: agents. Fields: id, name, personality_prompt, created_at.'" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/cookbooks/trees/aeries-2-simulation.yaml b/cookbooks/trees/aeries-2-simulation.yaml index 90aa278..b5acb7a 100644 --- a/cookbooks/trees/aeries-2-simulation.yaml +++ b/cookbooks/trees/aeries-2-simulation.yaml @@ -24,7 +24,7 @@ steps: prompt: "/extract-service core-api/internal/domain/agent_logic simulation-svc --pattern strangler" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .vars.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .vars.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/cookbooks/trees/aeries-3-society.yaml b/cookbooks/trees/aeries-3-society.yaml index 4d74681..2f0221d 100644 --- a/cookbooks/trees/aeries-3-society.yaml +++ b/cookbooks/trees/aeries-3-society.yaml @@ -38,7 +38,7 @@ steps: prompt: "/implement-feature {{ .vars.feature_slug }} --requirements 'Simulation SVC publishes agent moves to Redis. Spatial SVC tracks proximity. If two agents are near, Core UI shows a chat bubble.'" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .vars.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .vars.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/cookbooks/trees/evolving-app.yaml b/cookbooks/trees/evolving-app.yaml index 8b445b0..63e065c 100644 --- a/cookbooks/trees/evolving-app.yaml +++ b/cookbooks/trees/evolving-app.yaml @@ -62,7 +62,7 @@ steps: prompt: "/spec-feature {{ .vars.feature_slug }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/cookbooks/trees/full-stack-feature.yaml b/cookbooks/trees/full-stack-feature.yaml index b2183e9..f63b855 100644 --- a/cookbooks/trees/full-stack-feature.yaml +++ b/cookbooks/trees/full-stack-feature.yaml @@ -62,7 +62,7 @@ steps: prompt: "/spec-feature {{ .vars.feature_slug }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -92,7 +92,7 @@ steps: prompt: "/design-feature {{ .vars.feature_slug }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -135,7 +135,7 @@ steps: prompt: "/implement-task {{ .vars.feature_slug }} {{ .outputs.create-task-backend.task_id }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/cookbooks/trees/genkit-test.yaml b/cookbooks/trees/genkit-test.yaml index ef50f63..9706c11 100644 --- a/cookbooks/trees/genkit-test.yaml +++ b/cookbooks/trees/genkit-test.yaml @@ -92,7 +92,7 @@ steps: ' auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/cookbooks/trees/persona-community.yaml b/cookbooks/trees/persona-community.yaml index 5ab80ff..05978f7 100644 --- a/cookbooks/trees/persona-community.yaml +++ b/cookbooks/trees/persona-community.yaml @@ -24,16 +24,14 @@ steps: add-db: description: CockroachDB for persona + media persistence depends_on: [create-project] - on_error: continue action: api method: POST endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" body: { type: postgres, name: "persona-db" } add-redis: - description: Redis for SSE pub/sub and generation job queue (may already be provisioned by skeleton) + description: Redis for SSE pub/sub and generation job queue depends_on: [create-project] - on_error: continue action: api method: POST endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" @@ -98,7 +96,7 @@ steps: prompt: "/implement-feature persona-model --requirements 'DB migration in persona-api: CREATE TABLE personas (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, handle TEXT UNIQUE NOT NULL, gender TEXT NOT NULL, description TEXT NOT NULL, tags TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], spec_json JSONB, anchor_url TEXT, avatar_url TEXT, banner_url TEXT, image_urls TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], video_urls TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], status TEXT NOT NULL DEFAULT ''pending'', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()). Domain model Persona with same fields. PersonaService: Create(ctx, description, gender, customName string) (*Persona, error) — saves row then enqueues a generate_spec job. GetByID(ctx, id). List(ctx, limit, offset int) ([]*Persona, error). Queue job type PersonaGenerateJob {PersonaID string, Stage string} where Stage values are: spec, anchor, avatar, banner, gallery_batch, video. Endpoints all under /api/persona-api: POST /personas (body: {description, gender, custom_name?}, returns 202 with persona), GET /personas (query: limit=20, offset=0), GET /personas/{id}. All routes require JWT auth (use skeleton auth.Middleware). After any persona field update publish SSE event persona_updated to channel:personas via the SSE hub.'" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -120,7 +118,7 @@ steps: prompt: "/implement-feature persona-generation --requirements 'Implement the generation pipeline in media-worker using the skeleton ai-client package (LAOZHANG_API_KEY for images, GEMINI_API_KEY for video). Worker consumes PersonaGenerateJob from queue and executes the stage: STAGE spec — call LLM to generate a PersonaSpec JSON: {name, handle, gender, tags:[8 strings], personality_archetype, appearance:{hair, eyes, skin, body_type, distinctive_features}, style_signature, image_matrix:[{position:int, pose:string, clothing:string, background:string} x 30]}. Save full spec to spec_json, update name/handle/tags on the persona row. Then enqueue stage=anchor job. STAGE anchor — generate one reference face-front portrait image from appearance description. Save URL to anchor_url. Then enqueue stage=avatar AND stage=banner as parallel jobs. STAGE avatar — generate circular portrait derived from anchor appearance. Save to avatar_url. STAGE banner — generate 3:1 landscape lifestyle image derived from anchor. Save to banner_url. STAGE gallery_batch — generate 10 images using next 10 unfilled positions from image_matrix. Append URLs to image_urls array. If fewer than 30 images exist, enqueue another gallery_batch job. STAGE video — generate the next missing video from: [smile_reveal, personality_moment, lifestyle, invitation]. Append URL to video_urls. If fewer than 4 videos, enqueue next video job. After each stage: update persona row, publish job_update SSE event {persona_id, stage, status:complete|error, progress} to channel:personas.'" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -150,7 +148,7 @@ steps: prompt: "/implement-feature community-ui --requirements 'Build the React community UI using skeleton packages (@project/ui, @project/auth, @project/layout, @project/realtime, @project/api-client). App uses DashboardShell layout from @project/layout. Routes: /login (public) and / and /personas/:id (protected via ProtectedRoute from @project/auth). LOGIN PAGE: OTP flow — email input, Send Code button, then code input + Verify button. On success store token and redirect to /. COMMUNITY PAGE (/): Top bar has Create Persona button. Main area is a responsive grid of PersonaCard components. PersonaCard shows: banner_url as card background image, avatar_url as circular overlay (bottom-left), name + handle + up to 4 tags as pills, image/video count badges. While status=pending|generating show a subtle pulsing overlay with current stage label (Crafting identity... / Generating anchor... etc). Click card → navigate to /personas/:id. CREATE PANEL: slides in from right, contains: description textarea (placeholder: Describe your persona...), gender pill selector (Female / Male / Non-binary), optional Name field, Create button. On submit POST /api/persona-api/personas — new card appears immediately in grid with generating state. DETAIL PAGE (/personas/:id): full-width banner header, large circular avatar centered, name + handle + all tags. Image gallery grid (masonry or even grid, click any image to open fullscreen lightbox with arrow nav). Videos section shows 4 video players (smile reveal, personality moment, lifestyle, invitation) with labels. REALTIME: useEventChannel from @project/realtime subscribed to channel:personas. On persona_updated event refresh that card data. On job_update event show stage progress on the card overlay.'" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/cookbooks/trees/slackpath-1-authenticated-service.yaml b/cookbooks/trees/slackpath-1-authenticated-service.yaml index 8caee35..2baed67 100644 --- a/cookbooks/trees/slackpath-1-authenticated-service.yaml +++ b/cookbooks/trees/slackpath-1-authenticated-service.yaml @@ -93,7 +93,7 @@ steps: prompt: "/implement-feature {{ .vars.feature_slug }} --requirements 'User model with email/password. POST /register, POST /login (returns JWT). Middleware that checks Authorization header. GET /me returns user profile.'" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/cookbooks/trees/slackpath-2-async-worker-pipeline.yaml b/cookbooks/trees/slackpath-2-async-worker-pipeline.yaml index 37d7a88..f4526c8 100644 --- a/cookbooks/trees/slackpath-2-async-worker-pipeline.yaml +++ b/cookbooks/trees/slackpath-2-async-worker-pipeline.yaml @@ -20,9 +20,8 @@ steps: - domain: .data.domain add-redis: - description: Add Redis for job queue (may already exist from skeleton) + description: Add Redis for job queue depends_on: [create-project] - on_error: continue action: api method: POST endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" @@ -59,7 +58,7 @@ steps: prompt: "/implement-feature {{ .vars.feature_slug }} --requirements 'API: POST /jobs pushes JSON to Redis. Worker: Pops from Redis, simulates work, updates status. API: GET /jobs/{id} returns status.'" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/cookbooks/trees/slackpath-3-realtime-chat.yaml b/cookbooks/trees/slackpath-3-realtime-chat.yaml index 175fb5a..11dfda3 100644 --- a/cookbooks/trees/slackpath-3-realtime-chat.yaml +++ b/cookbooks/trees/slackpath-3-realtime-chat.yaml @@ -20,9 +20,8 @@ steps: - domain: .data.domain add-redis: - description: Add Redis for Pub/Sub (may already exist from skeleton) + description: Add Redis for Pub/Sub depends_on: [create-project] - on_error: continue action: api method: POST endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" @@ -56,7 +55,7 @@ steps: prompt: "/implement-feature {{ .vars.feature_slug }} --requirements 'GET /ws upgrades to websocket. Incoming messages are published to Redis channel. Redis subscriber broadcasts to all connected clients.'" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/cookbooks/trees/slackpath-4-microservice-constellation.yaml b/cookbooks/trees/slackpath-4-microservice-constellation.yaml index 72b6e9f..e3f27ec 100644 --- a/cookbooks/trees/slackpath-4-microservice-constellation.yaml +++ b/cookbooks/trees/slackpath-4-microservice-constellation.yaml @@ -20,9 +20,8 @@ steps: - domain: .data.domain add-db: - description: Add CockroachDB for user/auth storage (may already exist from skeleton) + description: Add CockroachDB for user/auth storage depends_on: [create-project] - on_error: continue action: api method: POST endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" @@ -31,9 +30,8 @@ steps: name: "main-db" add-redis: - description: Add Redis for job queue and pub/sub (may already exist from skeleton) + description: Add Redis for job queue and pub/sub depends_on: [create-project] - on_error: continue action: api method: POST endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" @@ -97,7 +95,7 @@ steps: prompt: "/implement-feature {{ .vars.feature_slug }} --requirements 'Chat Service must call http://auth-svc/validate to check tokens. Chat Service must push to Redis queue for Worker. Worker must process tasks.'" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/cookbooks/trees/slackpath-5-full-lifecycle.yaml b/cookbooks/trees/slackpath-5-full-lifecycle.yaml index c50ddd6..8ab2715 100644 --- a/cookbooks/trees/slackpath-5-full-lifecycle.yaml +++ b/cookbooks/trees/slackpath-5-full-lifecycle.yaml @@ -25,7 +25,6 @@ steps: add-db: description: Add database for preferences storage depends_on: [create-project] - on_error: continue action: api method: POST endpoint: "/projects/{{ .outputs.create-project.project_id }}/components" @@ -95,7 +94,7 @@ steps: prompt: "/spec-feature {{ .vars.feature_slug }} --requirements 'CRUD API for user preferences. GET/PUT /preferences/{user_id}. Preferences are key-value pairs stored in DB. Support theme, language, notifications settings.'" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -140,7 +139,7 @@ steps: prompt: "/design-feature {{ .vars.feature_slug }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -170,7 +169,7 @@ steps: prompt: "/breakdown-feature {{ .vars.feature_slug }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -200,7 +199,7 @@ steps: prompt: "/create-qa-plan {{ .vars.feature_slug }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -269,7 +268,7 @@ steps: prompt: "/implement-feature {{ .vars.feature_slug }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -312,7 +311,7 @@ steps: prompt: "/review-feature {{ .vars.feature_slug }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -355,7 +354,7 @@ steps: prompt: "/audit-feature {{ .vars.feature_slug }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id @@ -398,7 +397,7 @@ steps: prompt: "/run-qa {{ .vars.feature_slug }}" auto_commit: true auto_push: true - git_clone_url: "https://git.threesix.ai/jordan/{{ .outputs.create-project.project_id }}.git" + git_clone_url: "https://git.threesix.ai/threesix/{{ .outputs.create-project.project_id }}.git" outputs: - build_id: .data.task_id diff --git a/internal/adapter/gitea/client.go b/internal/adapter/gitea/client.go index 9674698..4b1cbf2 100644 --- a/internal/adapter/gitea/client.go +++ b/internal/adapter/gitea/client.go @@ -76,17 +76,9 @@ func (c *Client) CreateRepo(ctx context.Context, name, description string, priva DefaultBranch: "main", } - var repo *gitea.Repository - var err error - - // Try to create as org repo first, fall back to user repo - repo, _, err = c.client.CreateOrgRepo(c.defaultOwner, opts) + repo, _, err := c.client.CreateOrgRepo(c.defaultOwner, opts) if err != nil { - // May not be an org, try as user repo - repo, _, err = c.client.CreateRepo(opts) - if err != nil { - return nil, fmt.Errorf("failed to create repo: %w", err) - } + return nil, fmt.Errorf("failed to create repo %s/%s: %w", c.defaultOwner, name, err) } return repoFromGitea(repo), nil diff --git a/internal/adapter/templates/templates/skeleton/.gitignore b/internal/adapter/templates/templates/skeleton/.gitignore index 49f0256..e43ddaa 100644 --- a/internal/adapter/templates/templates/skeleton/.gitignore +++ b/internal/adapter/templates/templates/skeleton/.gitignore @@ -35,6 +35,8 @@ go.work.sum # Node node_modules/ .npm/ +.pnpm-store/ +.pnpm-store pnpm-lock.yaml # Shared packages diff --git a/internal/claudebox/git.go b/internal/claudebox/git.go index f91303f..34a40f0 100644 --- a/internal/claudebox/git.go +++ b/internal/claudebox/git.go @@ -208,7 +208,7 @@ func (g *GitOperations) CommitAndPush(ctx context.Context, workDir, message stri result.Error = fmt.Errorf("git diff: %w", err) return result } - for _, f := range strings.Split(strings.TrimSpace(diffOutput), "\n") { + for f := range strings.SplitSeq(strings.TrimSpace(diffOutput), "\n") { if f != "" { result.FilesChanged = append(result.FilesChanged, f) } @@ -241,12 +241,12 @@ func (g *GitOperations) CommitAndPush(ctx context.Context, workDir, message stri } // Pull before push to handle concurrent builds that may have advanced the remote. - if err := g.runGit(ctx, workDir, "pull", "--rebase", "origin", "HEAD"); err != nil { + if err := g.runGit(ctx, workDir, "pull", "--rebase", "origin", "main"); err != nil { g.logger.Warn("git pull --rebase before push failed, attempting push anyway", "error", err, "work_dir", workDir) } - if err := g.runGit(ctx, workDir, "push", "origin", "HEAD"); err != nil { + if err := g.runGit(ctx, workDir, "push", "origin", "HEAD:main"); err != nil { result.Error = fmt.Errorf("git push: %w", err) return result } @@ -299,8 +299,7 @@ func (g *GitOperations) Status(ctx context.Context, workDir string) (*GitStatusR return result, fmt.Errorf("git status: %w", err) } - lines := strings.Split(strings.TrimSpace(status), "\n") - for _, line := range lines { + for line := range strings.SplitSeq(strings.TrimSpace(status), "\n") { if len(line) > 3 { result.ChangedFiles = append(result.ChangedFiles, strings.TrimSpace(line[3:])) } diff --git a/internal/worker/pod_git_operations.go b/internal/worker/pod_git_operations.go index 51e2824..26ab089 100644 --- a/internal/worker/pod_git_operations.go +++ b/internal/worker/pod_git_operations.go @@ -98,8 +98,8 @@ func (g *PodGitOperations) CloneRepo(ctx context.Context, podName, workDir, clon // Strip token from currentRemote for comparison, since clone stores the authenticated URL // Format: https://token:TOKEN@host/path -> https://host/path normalizedRemote := currentRemote - if idx := strings.Index(currentRemote, "@"); idx != -1 && strings.HasPrefix(currentRemote, "https://") { - normalizedRemote = "https://" + currentRemote[idx+1:] + if _, after, found := strings.Cut(currentRemote, "@"); found && strings.HasPrefix(currentRemote, "https://") { + normalizedRemote = "https://" + after } expectedURL := cloneURL @@ -265,7 +265,7 @@ func (g *PodGitOperations) CommitAndPush(ctx context.Context, podName, workDir, result.Error = fmt.Errorf("git diff: %w", err) return result } - for _, f := range strings.Split(strings.TrimSpace(diffOutput), "\n") { + for f := range strings.SplitSeq(strings.TrimSpace(diffOutput), "\n") { if f != "" { result.FilesChanged = append(result.FilesChanged, f) } @@ -308,14 +308,14 @@ func (g *PodGitOperations) CommitAndPush(ctx context.Context, podName, workDir, // Pull before push to handle concurrent builds that may have advanced the remote. // Use --rebase to replay our commit on top of any new remote commits. - if err := g.runGitInPod(ctx, podName, workDir, "pull", "--rebase", "origin", "HEAD"); err != nil { + if err := g.runGitInPod(ctx, podName, workDir, "pull", "--rebase", "origin", "main"); err != nil { log.Warn("git pull --rebase before push failed, attempting push anyway", logging.FieldPodName, podName, logging.FieldError, err, ) } - if err := g.runGitInPod(ctx, podName, workDir, "push", "origin", "HEAD"); err != nil { + if err := g.runGitInPod(ctx, podName, workDir, "push", "origin", "HEAD:main"); err != nil { result.Error = fmt.Errorf("git push: %w", err) return result }