diff --git a/DECISIONS.md b/DECISIONS.md index f939898..3290543 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -118,3 +118,24 @@ When berget.ai tokens run out, flip `BRAIN_LLM_PRIMARY_URL` to iguana. **Decision**: The root context-sync generates a `mathias.md` prompt and `mathias.toml` agent config in `~/.vibe/`. This is the one tool that needs a custom adapter path. **Consequences**: Run `vibe --agent mathias` to use your conventions. Other Vibe users on the machine aren't affected. + +--- + +## 2026-05-18 — project_create commits staging namespace directly to infra main + +**Context:** `project_create` writes a k8s namespace manifest into the infra +repo so Flux brings up a staging environment for the new project. Initial +implementation pushed to a `staging/` branch, which required manual PR +merge before Flux saw the namespace — defeating the "one tool call, project +exists, staging reconciling within 60s" goal. + +**Decision:** Option A — commit directly to `main`. `callInfraCommit` passes +`branch: "main"` to gitea-mcp's `file_write_branch`; no PR, no merge step. + +**Consequences:** Staging namespace appears in cluster within ~60s of the +`project_create` call. Consistent with project-wide TBD policy (CLAUDE.md): +commit directly to main, every commit deployable. Acceptable because the +manifest is a fresh namespace under `k3s/staging//` — isolated, low +blast-radius, and Flux will simply recreate it if the file is bad. Manual +review gating was friction for no compensating safety gain on experiment +namespaces. diff --git a/internal/skills/project/handlers.go b/internal/skills/project/handlers.go index a3279fa..162d1f0 100644 --- a/internal/skills/project/handlers.go +++ b/internal/skills/project/handlers.go @@ -97,8 +97,7 @@ func (s *Skill) handleCreate(ctx context.Context, raw json.RawMessage) (json.Raw // Step 3: commit staging namespace manifest to infra repo. Done before // the issue so the staging env is reconciling by the time the issue lands. - branch := fmt.Sprintf("staging/%s", args.Name) - if err := s.callInfraCommit(ctx, args.Name, branch); err != nil { + if err := s.callInfraCommit(ctx, args.Name); err != nil { if !isConflict(err) { return marshalPartial(res, stepInfraCommit, err) } @@ -171,17 +170,16 @@ func (s *Skill) callMirror(ctx context.Context, name string) error { }, nil) } -// callInfraCommit writes the staging namespace manifest into the infra repo -// on a dedicated branch. Flux picks it up after merge. -func (s *Skill) callInfraCommit(ctx context.Context, name, branch string) error { +// callInfraCommit writes the staging namespace manifest directly to infra +// main. Flux reconciles within ~60s. See DECISIONS.md 2026-05-18. +func (s *Skill) callInfraCommit(ctx context.Context, name string) error { manifest := stagingNamespaceManifest(name, time.Now().UTC().Format(time.RFC3339)) return s.cfg.Client.CallTool(ctx, "file_write_branch", map[string]any{ "owner": s.cfg.GiteaOwner, "name": s.cfg.InfraRepo, "path": fmt.Sprintf("k3s/staging/%s/namespace.yaml", name), "content": manifest, - "branch": branch, - "base": "main", + "branch": "main", "message": fmt.Sprintf("feat(staging): add namespace for %s\n\nGenerated by hyperguild project_create.", name), }, nil) }