Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions docs/memory/wt-cli/create-output-phases.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ description: "Phase-separator output contract for `wt create` / `wt init` — Gi

> Post-implementation behavior capture for the `wt create` / `wt init` phase-separator output.
> Source change: `260531-pnmi-add-phase-separators`
> (single `wt.Warn` warning emitter + `wt delete` stdout→stderr warning realignment added by `260622-log5-dx-copy-polish`).
> (single `wt.Warn` warning emitter + `wt delete` stdout→stderr warning realignment added by `260622-log5-dx-copy-polish`;
> Open phase now reachable after init failure on the open-anyway *Yes* path, and the stdout path line suppressed on init-failure paths, by `260626-n6ma-create-init-failure-open-anyway`).

This file documents the phase-separator output contract that `wt create` and `wt init` honor, and — as the canonical stdout/stderr stream-discipline file — the single `wt.Warn` warning emitter that all `Warning:` call sites route through. Future changes touching `src/cmd/wt/create.go`, `src/cmd/wt/init.go`, `src/cmd/wt/delete.go`, or `src/internal/worktree/{crud.go,errors.go}` should preserve these invariants unless an explicit spec amendment supersedes them.

Expand All @@ -32,7 +33,7 @@ This file documents the phase-separator output contract that `wt create` and `wt
- A separator is emitted **only when its phase produces output** — a separator never precedes a phase that emits nothing:
- **Git** is emitted on every successful create, because the deferred summary block always prints (it is the Git phase's output).
- **Init** is emitted only when the init phase actually runs an init command — NOT when init is disabled and NOT on the `*InitNotFound` path.
- **Open** is emitted only when the open phase runs, gated on `worktreeOpen != "skip"`. `--worktree-open=skip` (and `--non-interactive`, which defaults open to `skip`) suppress it.
- **Open** is emitted only when the open phase runs, gated on `worktreeOpen != "skip"`. `--worktree-open=skip` (and `--non-interactive`, which defaults open to `skip`) suppress it. As of `260626-n6ma`, the Open phase is **also reachable after an init failure** on the interactive open-anyway *Yes* path — previously the Open separator + menu were reached only on init **success**. On the open-anyway *No* path the failure branch sets `worktreeOpen = "skip"`, so the separator + menu are suppressed there. The stdout discipline is unaffected (see below): on any init-failure path the function exits `ExitInitFailed = 7` via the end-of-function `initFailed` guard, which sits **before** the stdout path-line print, so the path line is NOT emitted on an init-failure path (even after a successful open-anyway open).
- The Git separator MUST stay inside the existing deferred-summary emission, **before** the init-phase `signal.Reset(syscall.SIGINT, syscall.SIGTERM)` call. It MUST NOT introduce new I/O or a prompt inside the tight reinstall window between `git worktree add` returning and that `signal.Reset` (see `init-failure-contract.md` "SIGINT during init").
- Existing summary/warning/prompt substrings are preserved verbatim — in particular `Created worktree:` and `Open in:`.

Expand All @@ -48,7 +49,8 @@ This file documents the phase-separator output contract that `wt create` and `wt
### STDOUT discipline: separators are stderr-only

- All phase separators are written to **stderr**. None ever appears on stdout.
- `wt create`'s stdout remains **solely** the final worktree-path line (`fmt.Println(wtPath)`), byte-identical to before this change. This preserves the launcher-contract guarantee that stdout is the machine-readable result. No separator, summary, or init output leaks to stdout.
- `wt create`'s stdout remains **solely** the final worktree-path line (`fmt.Println(wtPath)`), byte-identical to before this change, **on the success path**. This preserves the launcher-contract guarantee that stdout is the machine-readable result. No separator, summary, banner, prompt, or init output leaks to stdout.
- **On any init-failure path the path line is NOT printed** (`260626-n6ma`). The end-of-function guard `if initFailed { os.Exit(wt.ExitInitFailed) }` (`create.go:476–478`) sits **before** `fmt.Println(wtPath)` (`create.go:482`), so a non-zero init exit — including after a successful interactive open-anyway open — exits 7 with **no** stdout path line. This is the correct stdout discipline: stdout is the success result, and an init failure is not a success. Operators detect the kept worktree via exit code 7 (and the stderr banner's absolute `Worktree:` path / `Go:` hint), not via a stdout line. The init-failure banner and the open-anyway prompt are stderr writes, consistent with stdout=machine-result / stderr=human.
Comment on lines +52 to +53

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — moved the ConfirmYesNo prompt emission to stderr (menu.go), so the contract text is now accurate: the open-anyway prompt is a stderr write and no prompt leaks to stdout. Resolved by fixing the implementation to match the documented contract rather than weakening the doc. (f01602a)

- `wt init` was **realigned** so ALL its init diagnostics go to stderr: the `Running worktree init...` / `Worktree init complete.` banner (now `fmt.Fprintln(os.Stderr, ...)`), the Init separator, AND the init script's own stdout (the init child is wired with `cmd.Stdout = os.Stderr` at `init.go:83`, joining `cmd.Stderr = os.Stderr`). `wt init` has **no machine-readable stdout result** — it is a side-effecting command whose outcome is its exit code — so all of its output is diagnostic.
- The realignment changed only the stream (stdout → stderr) for `wt init`: exit-code behavior (`ExitInitFailed = 7` on script non-zero exit), graceful-skip behavior, and all output text are unchanged. Existing `wt init` tests assert on the combined `r.Stdout + r.Stderr`, so they stay green.

Expand Down Expand Up @@ -89,7 +91,7 @@ Deterministic output for unit tests; no dependency on terminal/ioctl; consistent
## Cross-references

- Spec doc: none — phase-separator output is per-command diagnostic structure, not part of `docs/specs/cli-surface.md`'s per-subcommand flag surface. This contract lives in memory only.
- Source: `src/internal/worktree/errors.go` (`PhaseSeparator`, `phaseSeparatorWidth`, `Warn`, the `NO_COLOR`-blanking `init()`), `src/internal/worktree/crud.go` (`RunWorktreeSetupWithObserver` Init separator emission), `src/cmd/wt/create.go` (Git + Open separators, stdout path line, `wt.Warn` call sites), `src/cmd/wt/init.go` (`runInitScript` Init separator + stderr realignment), `src/cmd/wt/delete.go` (`wt.Warn` call sites — incl. the two pre-menu warnings realigned stdout→stderr with `fmt.Fprintln(os.Stderr)` spacing framing).
- Source: `src/internal/worktree/errors.go` (`PhaseSeparator`, `phaseSeparatorWidth`, `Warn`, the `NO_COLOR`-blanking `init()`), `src/internal/worktree/crud.go` (`RunWorktreeSetupWithObserver` Init separator emission), `src/cmd/wt/create.go` (Git + Open separators, stdout path line gated behind the end-of-function `initFailed` exit-7 guard per `260626-n6ma`, `wt.Warn` call sites), `src/cmd/wt/init.go` (`runInitScript` Init separator + stderr realignment), `src/cmd/wt/delete.go` (`wt.Warn` call sites — incl. the two pre-menu warnings realigned stdout→stderr with `fmt.Fprintln(os.Stderr)` spacing framing).
- Tests: `src/internal/worktree/errors_test.go` (`PhaseSeparator` unit test: label presence, colored ANSI + `─`, NO_COLOR ASCII `-` with no `\033[`, 40-column visible width, no trailing newline; `260622-log5`: `Warn` unit test — color-wrapped `Warning:` to stderr when colored, plain `Warning: ` with no ANSI under blanked color vars); `src/cmd/wt/create_test.go` (`Created worktree:` stderr + one-line-stdout guard); `src/cmd/wt/init_test.go` (combined-stream `Running worktree init` / `Worktree init complete`); `src/cmd/wt/delete_test.go` (`260622-log5`: the uncommitted-changes / unpushed-commits warnings appear on stderr, not stdout).
- Constitution: Principle I (Single-Binary CLI, No Hidden State — motivated the fixed width, no terminal query), Principle V (Internal Package Boundary — `Warn` lives in `internal/worktree`, keeping `cmd/` thin), Principle VI (Interactive by Default, Scriptable on Demand — stdout=machine-result keeps `wt create`'s path line deterministic for launchers/operators).
- Sibling memory: `wt-cli/init-failure-contract.md` — the init runner / resolver / `*InitNotFound` contract this change builds on (the Init separator sits next to the `Running worktree init` line and respects the not-found and reinstall-window invariants). `/wt-cli/go-command-contract.md` — `wt go`'s stderr navigation confirmation (`260622-log5`) honors this same stdout=machine / stderr=human stream discipline. `wt-cli/list-status-contract.md` and `wt-cli/menu-navigation-contract.md` — same post-change invariant-capture pattern for sibling `wt` subcommands.
- Sibling memory: `wt-cli/init-failure-contract.md` — the init runner / resolver / `*InitNotFound` contract this change builds on (the Init separator sits next to the `Running worktree init` line and respects the not-found and reinstall-window invariants); as of `260626-n6ma` it also owns the interactive open-anyway prompt + `Go:` banner hint that make the Open phase reachable after an init failure and keep the stdout path line suppressed on exit 7. `/wt-cli/go-command-contract.md` — `wt go`'s stderr navigation confirmation (`260622-log5`) honors this same stdout=machine / stderr=human stream discipline. `wt-cli/list-status-contract.md` and `wt-cli/menu-navigation-contract.md` — same post-change invariant-capture pattern for sibling `wt` subcommands.
2 changes: 1 addition & 1 deletion docs/memory/wt-cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ description: "Behavior contracts for the `wt` CLI binary — commands, exit code
| [go-command-contract](go-command-contract.md) | `wt go` worktree-selection contract — selection-only navigation via `WT_CD_FILE`/stdout (no launch), exit codes, the current-worktree-included menu, and the `wt open --go` composition. |
| [help-dump-contract](help-dump-contract.md) | Contract for the hidden `wt help-dump` command — the JSON envelope shll.ai's scheduled puller consumes. |
| [idle-staleness-contract](idle-staleness-contract.md) | The shared idle predicate, the `wt delete --stale` selector, and the safety invariant that idleness never gates a deletion on its own. |
| [init-failure-contract](init-failure-contract.md) | Init-failure behavior of `wt create` / `wt init` — kept-worktree contract, `ExitInitFailed`, SIGINT handling, and terminal-foreground reclaim. |
| [init-failure-contract](init-failure-contract.md) | Init-failure behavior of `wt create` / `wt init` — kept-worktree contract, `ExitInitFailed` on every path, the interactive open-anyway prompt, the `wt go` banner hint, SIGINT handling, and terminal-foreground reclaim. |
| [list-status-contract](list-status-contract.md) | `wt list` output contract — enrichment-free default, `--status` opt-in dashboard, `--sort` ordering, and pointer-field JSON shape. |
| [menu-navigation-contract](menu-navigation-contract.md) | Arrow-key navigation contract for the shared `ShowMenu` — TTY gating, keybindings, and a byte-identical non-TTY fallback. |
| [recency-ordering-contract](recency-ordering-contract.md) | The single recency definition (`RecencyOf`/`RecencyLess`/`SortByRecency`) and newest-first ordering across `wt list`, `wt open`, and `wt delete`. |
Expand Down
Loading
Loading