From 6f899e2e83355a070ee6bac45adc53e1315b21be Mon Sep 17 00:00:00 2001 From: Than Tibbetts Date: Thu, 1 Oct 2026 19:46:17 -0400 Subject: [PATCH] Teach one next action per Needs-you entry An entry in the human-blocker section is one physical step in one place: one tool or site, one verb, finishable in one sitting. A Next: line that says "and" or names two places is two entries, and each carries the direct link and the exact values to paste, so nothing has to be looked up. Co-Authored-By: Claude Sonnet 5.5 --- README.md | 2 ++ sections.go | 1 + sections_test.go | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/README.md b/README.md index 2b73225..a4c7bef 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ Any split-pane setup works β€” [Supacode], tmux, or your terminal’s built-in s Two flags opt out of a default: `--no-claude` skips the `CLAUDE.md` note and reconcile hook; `--keep-board` leaves a legacy root `SIDECAR.md` where it is and points `init` at it directly, instead of migrating to `.sidecar/` (a no-op when `.sidecar/sidecar.md` already exists). `--yes`/`-y` skip the section picker described next, and keep `init` non-interactive end to end: from a terminal, a bare `sidecar init` opens the viewer on the board it just created, while `--yes` returns to the shell for scripts and muscle memory that need it to. Creating a brand-new board from an interactive terminal still opens a short picker for choosing its sections; pass a custom path (`sidecar init notes.md`) to place the board somewhere other than `.sidecar/sidecar.md`, which still gets the same automatic exclude, note, and hook. +The note in `CLAUDE.md` is upgraded in place whenever you re-run `sidecar init`, so a repo picks up new entry guidance β€” such as one next action per 🧠 entry β€” only when you run it there again. + Boards are per-directory. `sidecar init` refuses a board that is a symlink β€” the file itself, or the `.sidecar/` home it sits in β€” and writes nothing at all: no board, no `CLAUDE.md` note, no reconcile hook, so a linked board can't be re-inited to pick up a newer note until the link is gone. A link points two checkouts at one queue, and a linked `.sidecar/` shares one `previous.md` besides, so the per-turn `sidecar diff` reports another session's changes as this one's. Reading is unchanged: the viewer still opens a symlinked board that already exists. ## What it does diff --git a/sections.go b/sections.go index 6846244..2707fc9 100644 --- a/sections.go +++ b/sections.go @@ -159,6 +159,7 @@ func entryStyleRules(sections []Section, bullet string) string { b.WriteString(" β€” it belongs in a later section") } b.WriteString(" until it needs a decision, and then the `Next:` line asks for that decision.\n") + b.WriteString("\nEach `" + human.Header() + "` entry is one next action: one physical step, in one place β€” one tool or site, one verb β€” that the human can finish in one sitting. A `Next:` line that says \"and\" or names two places is two entries. Put what they need to act in the entry itself: the direct link, and the exact values to paste, so nothing has to be looked up.\n") } b.WriteString("\nWhen a turn ends, leave its result as an entry and, under it, an indented `Ask:` line with one narrative question β€” \"what did you change on your side?\", \"how did the deploy go?\" β€” never a yes/no. The human sees it as `? …` and answers under it; the answer arrives as an `Answer:` line, and sidecar's hook reports it after your next tool call, so read it, act on it, then clear the `Ask:` and `Answer:` lines or move the entry. The human can also click any entry's bullet, which turns it to `βœ“` (`- [x]`): read that as \"I did this\". The human never moves entries between sections β€” filing them stays yours.\n") return b.String() diff --git a/sections_test.go b/sections_test.go index 2a2431a..980ae05 100644 --- a/sections_test.go +++ b/sections_test.go @@ -178,3 +178,25 @@ func TestEntryStyleExampleDropsQueuePairWithoutAgentSection(t *testing.T) { t.Errorf("queue pair missing or misdirected with πŸ€– present:\n%s", full) } } + +// A Needs-you entry is one physical action in one place, with everything +// needed to act inside it β€” and the rule names the section by its own header, +// so it never points at a heading the board lacks. +func TestEntryStyleRulesTeachOneNextAction(t *testing.T) { + got := entryStyleRules(defaultSections(), "- ") + for _, want := range []string{ + "one next action", + "one tool or site, one verb", + "one sitting", + "is two entries", + "the direct link, and the exact values to paste", + } { + if !strings.Contains(got, want) { + t.Errorf("rules missing %q:\n%s", want, got) + } + } + noHuman := []Section{{"🚧", "In progress", "actively being worked"}} + if got := entryStyleRules(noHuman, "- "); strings.Contains(got, "one next action") { + t.Errorf("the rule names the human section and must vanish without one:\n%s", got) + } +}