diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ab9125d..6295e9b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,8 @@ repository. ## Prerequisites -- **Go 1.24+** - the module declares `go 1.24` and CI runs on 1.24. +- **Go** - the version declared in `go.mod` (CI builds with the same + version). - **tmux 3.1+** - required at runtime and for the integration test suite. `mox` uses tmux 3.1 features such as `split-window -l %`. - **golangci-lint** - used by `make lint` and by CI. Install from diff --git a/internal/cli/editor_state.go b/internal/cli/editor_state.go index c8a7508..8f3859b 100644 --- a/internal/cli/editor_state.go +++ b/internal/cli/editor_state.go @@ -154,14 +154,15 @@ func (st *editorState) applyDraft(d *sessionDraft) error { sessMap := findOrCreateMapKey(st.root.Content[0], "sessions") - // Guard against name collisions (I1: prevents duplicate keys in the map). + // A new or renamed session must not land on an existing key: yaml.v3 + // refuses to decode a mapping with duplicate keys. if !d.deleted && (d.orig == "" || d.name != d.orig) { if findMapKey(sessMap, d.name) != nil { return fmt.Errorf("session %q already exists", d.name) } } - // Snapshot the Content slice for rollback on write failure (C1). + // Snapshot Content so a failed write can roll the node tree back. snapshot := make([]*yaml.Node, len(sessMap.Content)) copy(snapshot, sessMap.Content) var didRename bool @@ -171,7 +172,8 @@ func (st *editorState) applyDraft(d *sessionDraft) error { case d.deleted: removeMapKey(sessMap, d.orig) default: - // M1: pure rename — only rename the key, don't re-encode the session body. + // Pure rename: rename the key only, so the body keeps its original + // formatting and comments instead of being re-encoded. if d.orig != "" && d.name != d.orig && sameSessionYAML(d.sess, st.cfg.Sessions[d.orig]) { didRename = true origName = d.orig diff --git a/internal/cli/editor_state_test.go b/internal/cli/editor_state_test.go index 893375b..95fe510 100644 --- a/internal/cli/editor_state_test.go +++ b/internal/cli/editor_state_test.go @@ -191,7 +191,7 @@ func TestApplyDraftStaleBlocksWrite(t *testing.T) { } } -// Regression test for C1: rollback on failed writes. +// A failed write must not leave a half-applied rename in the node tree. func TestApplyDraftRollbackOnWriteFailure(t *testing.T) { st := testEditorState(t, editorFixtureYAML) d := newDraft(st.cfg, "webfarm") @@ -247,7 +247,7 @@ func TestApplyDraftRollbackOnWriteFailure(t *testing.T) { } } -// Regression test for I1: collision guard. +// Renames and added sessions must not collide with an existing name. func TestApplyDraftRejectsDuplicateNames(t *testing.T) { st := testEditorState(t, editorFixtureYAML) @@ -281,7 +281,7 @@ func TestApplyDraftRejectsDuplicateNames(t *testing.T) { } } -// Regression test for I3: draft mutations don't leak into st.cfg. +// After a save, the draft and the typed config must not share memory. func TestApplyDraftNoAliasAfterSave(t *testing.T) { st := testEditorState(t, editorFixtureYAML) d := newDraft(st.cfg, "webfarm") @@ -308,7 +308,7 @@ func TestApplyDraftNoAliasAfterSave(t *testing.T) { } } -// Regression test for M1: pure rename preserves flow-style and comments. +// A pure rename keeps the session body's flow style and comments. func TestApplyDraftPureRenamePreservesFormat(t *testing.T) { st := testEditorState(t, editorFixtureYAML) diff --git a/internal/tmux/operations.go b/internal/tmux/operations.go index eb2df6f..f1a3a53 100644 --- a/internal/tmux/operations.go +++ b/internal/tmux/operations.go @@ -198,19 +198,6 @@ func (c *Client) SelectWindowByID(windowID string) error { return err } -// BaseIndex returns the global base-index option (default 0). -func (c *Client) BaseIndex() (int, error) { - out, err := c.Run("show-options", "-gv", "base-index") - if err != nil { - return 0, nil - } - idx, err := strconv.Atoi(strings.TrimSpace(out)) - if err != nil { - return 0, nil - } - return idx, nil -} - // SetPaneTitle sets the title of the target pane (visible in tmux's // pane-border-format if configured to show #{pane_title}). func (c *Client) SetPaneTitle(target, title string) error { @@ -252,21 +239,6 @@ func (c *Client) SelectLayout(windowTarget, layoutName string) error { return err } -// NewWindowInSession creates a new window in the named session and returns -// the new window's id. The session must already exist. Unlike CreateWindow, -// the new window is selected (active) immediately. -func (c *Client) NewWindowInSession(session, name, startDir string) (string, error) { - args := []string{"new-window", "-t", "=" + session, "-P", "-F", "#{window_id}", "-n", name} - if startDir != "" { - args = append(args, "-c", startDir) - } - out, err := c.Run(args...) - if err != nil { - return "", err - } - return strings.TrimSpace(out), nil -} - // WindowInfo describes a single window for inspection / import. type WindowInfo struct { ID string // tmux window id (@N)