@stylusnexus/work-plan 2026.6.15-1 → 2026.6.15-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -108,7 +108,7 @@ flowchart TB
108
108
  - `handoff <track> --set-next 4167,4148` — explicit numbers when you know exactly which issues are next.
109
109
  - Free-form via Claude in your agent session, which can review project memory and write a curated list back. The two `--*-next` flags are the no-LLM paths.
110
110
  - For tracks where you don't want to bother curating at all, set `next_up_auto: true` in the track's frontmatter — `brief` will then derive the list live each invocation, ignoring whatever's stored.
111
- - **Ranking presets** — when `next_up_auto: true` is on, the default ranking is `flow` (milestone → dependency → priority → recency). Override per-track with `set-next-up <track> --preset=<name>`, or set `next_up_default: <name>` in your config for a global fallback. Named presets: `flow` (the default), `priority-driven` (priority first, no milestone bias — good for backlogs with no milestones), `backlog` (oldest issues first — surfaces stalled work). Custom criterion order: `set-next-up <track> --order=aging,priority,dependency`. Clear a track's override with `--clear`.
111
+ - **Ranking presets** — when `next_up_auto: true` is on, the default ranking is `flow` (milestone → dependency → priority → recency). Override per-track with `set-next-up <track> --preset=<name>`, or set `next_up_default: <name>` in your config for a global fallback. Named presets: `flow` (the default), `priority-driven` (priority first, no milestone bias — good for backlogs with no milestones), `backlog` (oldest issues first — surfaces stalled work). Custom criterion order: `set-next-up <track> --order=aging,priority,dependency`. Clear a track's override with `--clear`. Toggle auto-derivation itself with `--auto=on|off` (no hand-editing frontmatter required).
112
112
  - **Weekly** → `hygiene` runs `refresh-md --all` + `reconcile --all` + `duplicates` in sequence to keep status icons, GitHub labels, and dedup state honest.
113
113
 
114
114
  > **When should I run `refresh-md`?** Any time you close or merge issues and want the track body to reflect the new state. `handoff` rewrites the status table for one track on every run, but `brief` reads GitHub live without writing anything back — so a track you haven't `handoff`'d recently stays stale on disk. `refresh-md <track>` (or **Sync Issue States from GitHub** in VS Code) fixes that on-demand; `hygiene` sweeps all tracks weekly.
@@ -523,7 +523,7 @@ See `docs/usage-examples.md` for end-to-end scenarios (morning brief, mid-work h
523
523
  | `remove-repo <key>` | Unregister a repo: delete its block from your config. **Config-only** — the notes folder, any tracks, and the local clone are left untouched (a notes folder or tracks that referenced it are now orphaned and can be removed by hand). Completes the add/update/remove trio with `init-repo`. |
524
524
  | `new-track <repo> <slug> [--priority=P0..P3] [--milestone=<m>]` | One-shot, non-interactive: create a new track file under `notes_root` for `<repo>` (a config key **or** an `org/repo` slug) with frontmatter. Unlike `init`, it makes the file for you — the headless creation path the VS Code viewer uses. |
525
525
  | `rename-track <old-slug \| old@repo> <new-slug> [--repo=<key>] [--fix-refs] [--commit]` | Rename an active track's slug: moves its `.md` file and updates the frontmatter `track` field + `last_touched`. Validates `<new-slug>` like `new-track` and rejects a name already taken in the same repo/tier. For shared tracks, `--commit` stages + commits the move (otherwise it prints a "commit to share" hint). `--fix-refs` rewrites sibling tracks' `depends_on` that reference the old slug; without it they're just warned about. Archived tracks are immutable. Public-repo gated. |
526
- | `set-next-up <track> (--preset=<name> \| --order=a,b,c \| --clear) [--repo=<key>] [--confirm=<token>]` | Configure the ranking preset used when `next_up_auto: true` computes a suggestion. `--preset` sets a named preset: `flow` (default — milestone → dependency → priority → recency), `priority-driven` (priority first, ignores milestone bias, for backlogs with no milestones), `backlog` (oldest issues first — surfaces stalled work), or `custom` (requires `--order`). `--order=a,b,c` sets a custom comma-separated criterion list (`milestone`, `dependency`, `priority`, `recency`, `aging`). `--clear` reverts to the global `next_up_default` config or the default `flow`. Writes `next_up_order` into the track's frontmatter; does NOT touch the `next_up` issue-list. Global default: add `next_up_default: <preset>` to `~/.claude/work-plan/config.yml`. Public-repo gated. |
526
+ | `set-next-up <track> (--preset=<name> \| --order=a,b,c \| --clear \| --auto=on\|off) [--repo=<key>] [--confirm=<token>]` | Configure the ranking preset and/or auto-derivation flag for a track's next_up list. `--preset` sets a named preset: `flow` (default — milestone → dependency → priority → recency), `priority-driven` (priority first, ignores milestone bias, for backlogs with no milestones), `backlog` (oldest issues first — surfaces stalled work), or `custom` (requires `--order`). `--order=a,b,c` sets a custom comma-separated criterion list (`milestone`, `dependency`, `priority`, `recency`, `aging`). `--clear` reverts to the global `next_up_default` config or the default `flow`. `--auto=on` activates `next_up_auto` so brief/orient/export auto-derive the next-up list live from the ranking preset (ignoring the curated list); `--auto=off` clears it to revert to the curated list. `--auto` can be used standalone or combined with `--preset`/`--order`/`--clear`. Writes `next_up_order` and/or `next_up_auto` into the track's frontmatter; does NOT touch the `next_up` issue-list. Global default: add `next_up_default: <preset>` to `~/.claude/work-plan/config.yml`. Public-repo gated. |
527
527
  | `set-notes-root <path>` | Relocate where your private track notes live (updates `notes_root` in config). Does not move existing tracks — it warns if any would be orphaned. |
528
528
  | `notes-vcs <init\|enable\|disable\|status\|undo> [<sha>] [--no-enable] [--json]` | Opt-in **local** version control for the private `notes_root` tier — history/undo for tracks on your machine, never pushed. `init` git-inits `notes_root` as a personal repo (baseline commit of existing tracks) and turns on auto-commit (`--no-enable` skips that). For safety it **refuses** a `notes_root` that already has a git remote or is a repo work-plan didn't create. When on, every track-mutating command (`slot`/`group`/`handoff`/`close`/`set`/…) commits **only the files it changed** (pre-existing uncommitted edits are left alone) as an undoable commit. The shared tier is unaffected — it's versioned by its own repo. `status` shows whether `notes_root` is a repo, whether auto-commit is on, and the last commit (`--json` for the machine shape the VS Code viewer polls). `undo [<sha>]` reverts a commit (default HEAD) — reverses the last edit. |
529
529
  | `push-track <track\|track@repo> [--repo=<key>] [--no-push] [--confirm=<token>]` | **Promote a private track to the shared tier and publish it** (#306). Moves the track's `.md` from `notes_root` into the repo's `.work-plan/` (on its `plan_branch`, via a worktree), removes the private copy so it isn't duplicated, commits to the plan branch, and pushes — unless `--no-push`. Tier is derived from location, so this is a file move, not a frontmatter edit. Requires a local clone + a configured `plan_branch` (else hints `plan-branch init`). Pushing to a **public** repo makes the track world-visible → confirm-token gated. |
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2026.06.15+a627bc9
1
+ 2026.06.15+c80ece1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stylusnexus/work-plan",
3
- "version": "2026.6.15-1",
3
+ "version": "2026.6.15-2",
4
4
  "description": "Track-aware daily work planning over GitHub issues. Shared tracks (git-synced .work-plan/ in each repo), AI clustering (group/auto-triage), VS Code viewer, Claude Code + Codex plugins. Pure Python stdlib.",
5
5
  "bin": {
6
6
  "work-plan": "bin/work-plan"
@@ -1,10 +1,20 @@
1
1
  """set-next-up subcommand — guarded edit of a track's next_up ranking preset.
2
2
 
3
3
  Usage:
4
- work_plan.py set-next-up <track> [--repo=<key>] (--preset=<name> | --order=a,b,c | --clear) [--confirm=<token>]
4
+ work_plan.py set-next-up <track> [--repo=<key>]
5
+ (--preset=<name> | --order=a,b,c | --clear | --auto=on|off)
6
+ [--confirm=<token>]
5
7
 
6
- Writes `next_up_order` into the track's frontmatter. Does NOT touch the
7
- `next_up` issue-list key.
8
+ Writes `next_up_order` and/or `next_up_auto` into the track's frontmatter.
9
+ Does NOT touch the `next_up` issue-list key.
10
+
11
+ --preset=<name> Set one of the named ranking presets (flow, priority-driven,
12
+ backlog) or 'custom' (which requires --order).
13
+ --order=a,b,c Set a custom comma-separated criterion list.
14
+ --clear Remove the next_up_order key (reverts to global/default).
15
+ --auto=on|off Toggle the next_up_auto flag. When on, brief/orient/export
16
+ auto-derive the next-up list via the ranking preset (#326).
17
+ Can be used standalone or combined with --preset/--order/--clear.
8
18
 
9
19
  Public-repo gated: without --confirm it prints {needs_confirm, reason, token}
10
20
  and makes no change. The VS Code extension surfaces that as a modal then
@@ -22,12 +32,12 @@ from lib.next_up import CRITERIA, PRESETS
22
32
 
23
33
  def run(args: list[str]) -> int:
24
34
  flags, positional = parse_flags(
25
- args, {"--confirm", "--repo", "--clear", "--preset", "--order"}
35
+ args, {"--confirm", "--repo", "--clear", "--preset", "--order", "--auto"}
26
36
  )
27
37
  if not positional:
28
38
  print(
29
39
  "usage: work_plan.py set-next-up <track> "
30
- "(--preset=<name> | --order=a,b,c | --clear) "
40
+ "(--preset=<name> | --order=a,b,c | --clear | --auto=on|off) "
31
41
  "[--repo=<key>] [--confirm=<token>]"
32
42
  )
33
43
  return 2
@@ -41,11 +51,27 @@ def run(args: list[str]) -> int:
41
51
  clear = bool(flags.get("--clear"))
42
52
  preset_flag = flags.get("--preset") if flags.get("--preset") is not True else None
43
53
  order_flag = flags.get("--order") if flags.get("--order") is not True else None
54
+ auto_raw = flags.get("--auto") if flags.get("--auto") is not True else None
55
+
56
+ # Parse and validate --auto value
57
+ auto_value = None # None means not specified
58
+ if auto_raw is not None:
59
+ auto_lower = auto_raw.lower() if isinstance(auto_raw, str) else ""
60
+ if auto_lower == "on":
61
+ auto_value = True
62
+ elif auto_lower == "off":
63
+ auto_value = False
64
+ else:
65
+ print(
66
+ f"ERROR: --auto must be 'on' or 'off', got {auto_raw!r}",
67
+ file=sys.stderr,
68
+ )
69
+ return 2
44
70
 
45
- # Must have at least one of --preset, --order, or --clear
46
- if not clear and preset_flag is None and order_flag is None:
71
+ # Must have at least one of --preset, --order, --clear, or --auto
72
+ if not clear and preset_flag is None and order_flag is None and auto_value is None:
47
73
  print(
48
- "ERROR: specify --preset=<name>, --order=a,b,c, or --clear",
74
+ "ERROR: specify --preset=<name>, --order=a,b,c, --clear, or --auto=on|off",
49
75
  file=sys.stderr,
50
76
  )
51
77
  return 2
@@ -123,8 +149,19 @@ def run(args: list[str]) -> int:
123
149
 
124
150
  if clear:
125
151
  track.meta.pop("next_up_order", None)
152
+ if auto_value is not None:
153
+ _apply_auto(track, auto_value)
126
154
  write_file(track.path, track.meta, track.body)
127
155
  print(f"✓ cleared next_up_order on {track.name}")
156
+ if auto_value is not None:
157
+ _print_auto_result(track, auto_value)
158
+ return 0
159
+
160
+ # If --auto is the only flag (no preset/order/clear), write just the auto flag.
161
+ if preset_flag is None and order_list is None and auto_value is not None:
162
+ _apply_auto(track, auto_value)
163
+ write_file(track.path, track.meta, track.body)
164
+ _print_auto_result(track, auto_value)
128
165
  return 0
129
166
 
130
167
  # Build the next_up_order mapping
@@ -145,10 +182,29 @@ def run(args: list[str]) -> int:
145
182
  )
146
183
 
147
184
  track.meta["next_up_order"] = nuo
185
+ if auto_value is not None:
186
+ _apply_auto(track, auto_value)
148
187
  write_file(track.path, track.meta, track.body)
149
188
 
150
189
  if preset_flag and preset_flag != "custom":
151
190
  print(f"✓ set next_up_order preset={preset_flag!r} on {track.name}")
152
191
  elif order_list is not None:
153
192
  print(f"✓ set next_up_order custom order={order_list!r} on {track.name}")
193
+ if auto_value is not None:
194
+ _print_auto_result(track, auto_value)
154
195
  return 0
196
+
197
+
198
+ def _apply_auto(track: "SimpleNamespace", auto_value: bool) -> None:
199
+ """Mutate track.meta to set or remove next_up_auto."""
200
+ if auto_value:
201
+ track.meta["next_up_auto"] = True
202
+ else:
203
+ track.meta.pop("next_up_auto", None)
204
+
205
+
206
+ def _print_auto_result(track: "SimpleNamespace", auto_value: bool) -> None:
207
+ if auto_value:
208
+ print(f"✓ set next_up_auto=true on {track.name}")
209
+ else:
210
+ print(f"✓ cleared next_up_auto on {track.name}")
@@ -124,10 +124,8 @@ def build_export(tracks, issues_by_track, visibility, now: str,
124
124
  in_progress_nums=in_progress_set,
125
125
  order=next_up_order,
126
126
  )
127
- next_up_is_auto = True
128
127
  else:
129
128
  next_up = [n for n in (t.meta.get("next_up") or []) if n not in closed_nums]
130
- next_up_is_auto = False
131
129
  out["tracks"].append({
132
130
  "name": t.name,
133
131
  "repo": t.repo,
@@ -155,9 +153,11 @@ def build_export(tracks, issues_by_track, visibility, now: str,
155
153
  "plan": plan_by_track.get(t.name),
156
154
  # Effective next_up ranking preset for this track (#326 Phase 2).
157
155
  "next_up_preset": next_up_preset_name,
158
- # True when `next_up` above was auto-derived from the ranking preset
159
- # (next_up_auto: true), not read from the curated list (#326).
160
- "next_up_auto": next_up_is_auto,
156
+ # True when the track has `next_up_auto: true` set in its frontmatter,
157
+ # meaning the next-up list is auto-derived from the ranking preset (#326).
158
+ # Reflects the SETTING, not whether derivation actually ran (so the
159
+ # viewer toggle shows On even when a track has zero open issues).
160
+ "next_up_auto": bool(t.meta.get("next_up_auto")),
161
161
  })
162
162
  out["untracked"] = [
163
163
  {"repo": repo, "issues": [normalize_issue(r) for r in rows]}
@@ -86,6 +86,31 @@ class BuildExportNextUpAutoTest(unittest.TestCase):
86
86
  self.assertEqual(tr["next_up"], [7]) # the curated list, unchanged
87
87
  self.assertFalse(tr["next_up_auto"])
88
88
 
89
+ def test_auto_on_with_zero_open_issues_still_exports_flag_true(self):
90
+ """next_up_auto: true + zero fetched issues → next_up_auto=True in export (the
91
+ SETTING, not whether auto-derivation actually ran). Viewer toggle must show
92
+ On even when there are no issues to rank.
93
+
94
+ When no issues are fetched, the auto-derivation branch does not run (there's
95
+ nothing to rank); the export still emits next_up_auto=True so the viewer
96
+ knows the flag is on. The next_up list itself falls through to the curated
97
+ list (which may be non-empty — that's acceptable and a separate concern)."""
98
+ # Use a track with no stored next_up to isolate the flag assertion cleanly.
99
+ meta = {"status": "active", "launch_priority": "P2", "milestone_alignment": "v1",
100
+ "blockers": [], "next_up": [], "depends_on": [],
101
+ "next_up_auto": True,
102
+ "github": {"repo": "o/r", "issues": []}}
103
+ from types import SimpleNamespace
104
+ from pathlib import Path
105
+ t = SimpleNamespace(name="t", repo="o/r", tier="private",
106
+ path=Path("/tmp/notes/t.md"), folder="myrepo", meta=meta)
107
+ out = build_export([t],
108
+ {("o/r", "t"): []}, # zero issues fetched
109
+ {"o/r": "PRIVATE"}, now="t")
110
+ tr = out["tracks"][0]
111
+ self.assertTrue(tr["next_up_auto"]) # flag reflects setting, not derivation
112
+ self.assertEqual(tr["next_up"], []) # no issues → empty list
113
+
89
114
 
90
115
  class BuildExportNextUpFilterTest(unittest.TestCase):
91
116
  """next_up entries whose issue is closed in the fetched payload are filtered out."""
@@ -197,5 +197,100 @@ class SetNextUpTest(unittest.TestCase):
197
197
  self.assertIn("--order is ignored", err.getvalue())
198
198
 
199
199
 
200
+ class SetNextUpAutoFlagTest(unittest.TestCase):
201
+ """Tests for --auto=on|off flag on set-next-up."""
202
+
203
+ def _drive_with_stderr(self, args, vis="PRIVATE", cfg=None, track=None):
204
+ """Like _drive but also captures stderr."""
205
+ base_cfg = {"notes_root": "/tmp"}
206
+ if cfg is not None:
207
+ base_cfg.update(cfg)
208
+ t = track if track is not None else _t()
209
+ with patch("commands.set_next_up.load_config", return_value=base_cfg), \
210
+ patch("commands.set_next_up.discover_tracks", return_value=[t]), \
211
+ patch("lib.write_guard.repo_visibility", return_value=vis), \
212
+ patch("commands.set_next_up.write_file") as mw:
213
+ out, err = io.StringIO(), io.StringIO()
214
+ with redirect_stdout(out), redirect_stderr(err):
215
+ rc = set_next_up.run(args)
216
+ return rc, mw, out.getvalue(), err.getvalue()
217
+
218
+ def test_auto_on_writes_next_up_auto_true(self):
219
+ """--auto=on sets next_up_auto: True in track meta."""
220
+ rc, mw, out, err = self._drive_with_stderr(["ph", "--auto=on"])
221
+ self.assertEqual(rc, 0)
222
+ mw.assert_called_once()
223
+ meta = mw.call_args[0][1]
224
+ self.assertTrue(meta.get("next_up_auto"))
225
+
226
+ def test_auto_off_removes_next_up_auto(self):
227
+ """--auto=off removes next_up_auto from track meta."""
228
+ t = _t(meta={"status": "active", "next_up_auto": True})
229
+ rc, mw, out, err = self._drive_with_stderr(["ph", "--auto=off"], track=t)
230
+ self.assertEqual(rc, 0)
231
+ mw.assert_called_once()
232
+ meta = mw.call_args[0][1]
233
+ self.assertNotIn("next_up_auto", meta)
234
+
235
+ def test_auto_off_on_track_without_key_still_succeeds(self):
236
+ """--auto=off on a track with no next_up_auto key still writes ok (no KeyError)."""
237
+ rc, mw, out, err = self._drive_with_stderr(["ph", "--auto=off"])
238
+ self.assertEqual(rc, 0)
239
+ mw.assert_called_once()
240
+ meta = mw.call_args[0][1]
241
+ self.assertNotIn("next_up_auto", meta)
242
+
243
+ def test_auto_bogus_returns_rc2_no_write(self):
244
+ """--auto=bogus → rc=2 and no write."""
245
+ rc, mw, out, err = self._drive_with_stderr(["ph", "--auto=bogus"])
246
+ self.assertEqual(rc, 2)
247
+ mw.assert_not_called()
248
+
249
+ def test_auto_standalone_private_writes(self):
250
+ """--auto=on alone (no --preset/--order/--clear) is accepted on private repo."""
251
+ rc, mw, out, err = self._drive_with_stderr(["ph", "--auto=on"])
252
+ self.assertEqual(rc, 0)
253
+ mw.assert_called_once()
254
+
255
+ def test_auto_standalone_public_needs_confirm(self):
256
+ """--auto=on alone on a PUBLIC repo → needs_confirm, no write."""
257
+ rc, mw, out, err = self._drive_with_stderr(["ph", "--auto=on"], vis="PUBLIC")
258
+ self.assertEqual(rc, 0)
259
+ mw.assert_not_called()
260
+ self.assertIn("needs_confirm", out)
261
+
262
+ def test_auto_standalone_public_with_valid_confirm_writes(self):
263
+ """--auto=on alone on PUBLIC repo with valid --confirm token proceeds to write."""
264
+ tok = make_token("o/r", "ph")
265
+ rc, mw, out, err = self._drive_with_stderr(
266
+ ["ph", "--auto=on", f"--confirm={tok}"], vis="PUBLIC"
267
+ )
268
+ self.assertEqual(rc, 0)
269
+ mw.assert_called_once()
270
+ meta = mw.call_args[0][1]
271
+ self.assertTrue(meta.get("next_up_auto"))
272
+
273
+ def test_auto_on_combined_with_preset_writes_both(self):
274
+ """--auto=on --preset=backlog sets BOTH next_up_auto AND next_up_order in one write."""
275
+ rc, mw, out, err = self._drive_with_stderr(["ph", "--auto=on", "--preset=backlog"])
276
+ self.assertEqual(rc, 0)
277
+ mw.assert_called_once()
278
+ meta = mw.call_args[0][1]
279
+ self.assertTrue(meta.get("next_up_auto"))
280
+ self.assertEqual(meta.get("next_up_order"), {"preset": "backlog"})
281
+
282
+ def test_auto_on_prints_success_message(self):
283
+ """--auto=on prints a clear success line."""
284
+ rc, mw, out, err = self._drive_with_stderr(["ph", "--auto=on"])
285
+ self.assertIn("next_up_auto", out)
286
+ self.assertIn("true", out.lower())
287
+
288
+ def test_auto_off_prints_success_message(self):
289
+ """--auto=off prints a clear success line."""
290
+ t = _t(meta={"status": "active", "next_up_auto": True})
291
+ rc, mw, out, err = self._drive_with_stderr(["ph", "--auto=off"], track=t)
292
+ self.assertIn("next_up_auto", out)
293
+
294
+
200
295
  if __name__ == "__main__":
201
296
  unittest.main()
@@ -167,10 +167,10 @@ DESCRIPTIONS = [
167
167
  "Guarded edit of a track's frontmatter fields (status, launch_priority, milestone_alignment, blockers, next_up). Validates field names + status values; blockers/next_up take comma-separated issue numbers. Setting `next_up` here writes ONLY the frontmatter field — for next_up plus a session-log entry (and a body refresh), use `handoff --set-next` instead. Writes into a PUBLIC repo only with a confirm token: without one it prints {needs_confirm, reason, token} and makes no change (the VS Code viewer surfaces that as a modal, then re-invokes with --confirm=<token>).",
168
168
  "Programmatic/GUI edits that have no dedicated verb — e.g. the VS Code extension changing a status or blockers list. On the terminal you'll usually use the named verbs instead.",
169
169
  "/work-plan set ux-redesign status=parked"),
170
- ("set-next-up", "<track | track@repo> (--preset=<name> | --order=a,b,c | --clear) [--repo=<key>] [--confirm=<token>]",
171
- "Configure the ranking preset for a track's auto next_up suggestion. --preset sets one of the named presets (flow, priority-driven, backlog) or 'custom' (which requires --order). --order=a,b,c sets a custom comma-separated criterion list (milestone, dependency, priority, recency, aging). --clear reverts to the global or default preset. Writes next_up_order into the track's frontmatter (does NOT touch the next_up issue list). Public-repo gated: without --confirm it prints {needs_confirm, reason, token} and makes no change.",
172
- "When you want a track to use a different ranking order than the default (flow). Use priority-driven for pure backlog work with no milestones, backlog to surface oldest stalled issues first.",
173
- "/work-plan set-next-up my-track --preset=priority-driven"),
170
+ ("set-next-up", "<track | track@repo> (--preset=<name> | --order=a,b,c | --clear | --auto=on|off) [--repo=<key>] [--confirm=<token>]",
171
+ "Configure the ranking preset and/or auto-derivation flag for a track's next_up list. --preset sets one of the named presets (flow, priority-driven, backlog) or 'custom' (which requires --order). --order=a,b,c sets a custom comma-separated criterion list (milestone, dependency, priority, recency, aging). --clear reverts to the global or default preset. --auto=on activates auto-derivation (brief/orient/export derive next-up live via the ranking preset instead of the curated list); --auto=off reverts to the curated list. --auto can be used standalone or combined with --preset/--order/--clear. Writes next_up_order and/or next_up_auto into the track's frontmatter (does NOT touch the next_up issue list). Public-repo gated: without --confirm it prints {needs_confirm, reason, token} and makes no change.",
172
+ "When you want a track to use a different ranking order than the default (flow), or to activate auto-derivation so the viewer always shows a freshly-ranked list. Use priority-driven for pure backlog work with no milestones, backlog to surface oldest stalled issues first.",
173
+ "/work-plan set-next-up my-track --preset=priority-driven --auto=on"),
174
174
  ("new-track", "<repo> <slug> [--priority=P0..P3] [--milestone=<m>] [--private] [--confirm=<token>]",
175
175
  "Create a brand-new track file under notes_root in one headless call. <repo> is either a configured key (e.g. 'myproject') or a bare org/repo slug (e.g. 'your-org/myproject'). Writes frontmatter with status=active and optional priority/milestone. Gates on public repos — prints {needs_confirm, token} and exits cleanly; re-run with --confirm=<token> to proceed.",
176
176
  "When a new feature branch or initiative starts and you want the track file created immediately — especially from a non-terminal caller like the VS Code extension that can't interactively run init.",