Skip to content

fix(cli): unblock cold-start bootstrap pipelines + materialise outputs - #1586

Merged
nextlevelshit merged 2 commits into
mainfrom
fix/onboard-project-cold-start
Apr 29, 2026
Merged

fix(cli): unblock cold-start bootstrap pipelines + materialise outputs#1586
nextlevelshit merged 2 commits into
mainfrom
fix/onboard-project-cold-start

Conversation

@nextlevelshit

Copy link
Copy Markdown
Collaborator

Summary

The 1.1 onboarder pipeline (PR #1581) merged with three latent chicken-and-egg gates that prevented `wave run onboard-project` from ever executing on a fresh repo — the exact scenario it was designed for. This PR fixes those gates and adds a materialisation hook so worktree-isolated outputs reach the project root.

Discovered during real-verification of #1581 against fresh `/tmp/wave-smoke-onboard-{go,node}` repos.

What this changes

Cold-start gate bypasses (bootstrap allowlist: `onboard-project`, `ops-bootstrap`):

  • `checkOnboarding(pipelineName)` exempts bootstrap pipelines from the sentinel/manifest gate
  • `loadManifestStrict` falls back to a synthesised in-memory manifest from embedded defaults
  • `pipeline.LoadByName` falls back to embedded pipeline yaml for the allowlist only — preserves "manifest owns pipeline resolution" for everything else
  • `claude.prepareWorkspace` reads base-protocol.md + persona prompts from embedded defaults when missing on disk
  • `runDetached` + `buildStateStore` mkdir-all `.agents/` so SQLite can open `state.db`

Materialisation hook:

  • New `materialiseBootstrapOutputs` runs after a successful bootstrap pipeline and copies sentinel, wave.yaml, output JSONs, and any custom personas/pipelines/prompts/contracts from worktree workspaces back to project root
  • Worktree isolation that's correct for impl-issue PRs would otherwise trap onboarding artefacts inside `.agents/workspaces//`
  • Files at project root are never overwritten — preserve-existing semantics

Pipeline yaml fix:

  • Removed `model: cheapest` defaults from onboard-project.yaml so CLI `--model` flag is respected (was forcing cheapest regardless)

Test plan

  • `go test ./cmd/wave/commands/... ./internal/pipeline/... ./internal/adapter/...` — green
  • `TestBaseProtocolMissingError` → `TestBaseProtocolEmbeddedFallback` — asserts new fallback semantics
  • Go cold-start smoke: `/tmp/wave-smoke-onboard-go` (`go.mod` + `main.go` only). 4/4 steps, 180s, 73k tokens. Sentinel + 4 output JSONs at project root. Detection: `flavour: go`, signals `go.mod` + `main.go`, parsed module name + go version
  • Node cold-start smoke: `/tmp/wave-smoke-onboard-node` (`package.json` + `index.js` only). 4/4 steps, 230s, 79k tokens. Sentinel + 3 output JSONs + generated `node-implementer.md` persona at project root. Detection: `flavour: node`, signals `package.json` + `index.js`, parsed `dummy v0.0.1`
  • Existing onboard tests on already-primed repos still work (CI)
  • No regression in non-bootstrap pipelines (`impl-issue`, `audit-*`, etc.) — gate behaviour unchanged for them

Related

The 1.1 onboarder pipeline (PR #1581) merged with three latent
chicken-and-egg gates that prevented `wave run onboard-project` from
ever executing on a fresh repo — the exact scenario it was designed for.

Fixes:

- `checkOnboarding(pipelineName)` now exempts a small bootstrap
  allowlist (`onboard-project`, `ops-bootstrap`). Other pipelines
  still require a primed `.agents/` or `wave.yaml`.

- `loadManifestStrict` falls back to a synthesised in-memory
  manifest from embedded defaults when a bootstrap pipeline runs
  with no on-disk wave.yaml. The pipeline is expected to write the
  real manifest as part of its output.

- `pipeline.LoadByName` falls back to embedded defaults for the
  bootstrap allowlist only, preserving the existing "manifest owns
  pipeline resolution" contract for everything else.

- `claude.prepareWorkspace` now reads base-protocol.md + persona
  prompts from embedded defaults when missing on disk, so the
  workspace can be assembled before scaffolding completes.

- `runDetached` + `buildStateStore` mkdir-all `.agents/` so SQLite
  can open `state.db` in a cold-start repo.

- New `materialiseBootstrapOutputs` post-pipeline hook copies the
  sentinel, wave.yaml, output JSONs, and any custom personas /
  pipelines / prompts / contracts from the run's worktree
  workspaces back to the project root. Without this the worktree
  isolation that's correct for impl-issue PRs traps the onboarding
  artefacts inside `.agents/workspaces/<run-id>/`.

- onboard-project.yaml: removed `model: cheapest` defaults so CLI
  `--model` flag is respected (was forcing cheapest regardless).

Real-verification (run on cold-start `/tmp/wave-smoke-onboard-{go,node}`
repos with `--adapter claude --model balanced --detach`):

- Go smoke: 4/4 steps, 180s, 73k tokens. Sentinel + 4 output JSONs
  at project root.
- Node smoke: 4/4 steps, 230s, 79k tokens. Sentinel + 3 output JSONs
  + generated `node-implementer.md` persona at project root.

Updates:
- TestBaseProtocolMissingError → TestBaseProtocolEmbeddedFallback,
  asserts new fallback semantics.
- All other tests in cmd/wave/commands and internal/pipeline still
  pass.
Moving the embed FS readers into internal/defaults/embedfs (a leaf
package with no internal/manifest dependency) lets internal/adapter
and internal/pipeline pull cold-start fallback content without
forming an import cycle through internal/manifest's permissions test.

- internal/defaults/embedfs: leaf package owning all embedded asset
  files (personas, pipelines, contracts, prompts, schemas, skills)
  + simple readers returning string maps
- internal/defaults: now a thin wrapper that re-exports the embedfs
  readers and adds the manifest-typed GetPersonaConfigs (only used
  by internal/onboarding, which transitively imports manifest)
- internal/adapter/claude.go: cold-start fallback for base-protocol.md
  + persona prompts now reads via embedfs (was internal/defaults,
  triggered the cycle)
- internal/pipeline/load_by_name.go: bootstrap embedded-pipeline
  fallback now reads via embedfs

Test path updates for sync_test.go + impl_finding_workspace_test.go +
all_pipelines_load_test.go to point at the new embedfs/* directories.
@nextlevelshit
nextlevelshit merged commit 033ed1c into main Apr 29, 2026
10 checks passed
@nextlevelshit
nextlevelshit deleted the fix/onboard-project-cold-start branch April 29, 2026 23:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant