=== tracker_enabled: no tracker configured
--- stdout
--- stderr
rc=1
=== tracker_enabled: a tracker path
--- stdout
--- stderr
rc=0
=== tracker_enabled: a blank-but-not-empty value
--- stdout
--- stderr
rc=0
=== tracker_enabled: unset under set -u
--- stdout
--- stderr
<where>: EXEC_QUEUE_FILE: unbound variable
rc=1
=== prune: dry run over mixed rows
--- stdout
Abandoned lane pruning — tags older than 30 day(s)
======================================================
MODE: DRY RUN — nothing is deleted. Re-run with --yes to apply.

REMOVE abandoned/T-1-20260901                                 48d  tag contains every branch commit
       branch:   lane/1/T-1
       worktree: <SB>/lanes/1
KEEP   abandoned/T-2-20260915                                 34d  branch moved past its tag
       branch:   lane/2/T-2
KEEP   abandoned/T-3-a-very-long-tag-name-that-overflows-the-column-20261001   18d  younger than the cut-off
REMOVE abandoned/T-4-20260801                                 79d  no branch left
       worktree: <SB>/lanes/with space
WEIRD  abandoned/T-5                                           ?d  unreadable age

------------------------------------------------------
2 removable, 2 kept.
A branch is deleted only when the abandoned TAG still contains every
commit on it. A branch that moved past its tag is live work and is left
alone; an untagged branch is never touched at all.

To apply: orchestrate.sh --prune-abandoned 30 --yes
--- stderr
CALL lane_prune_abandoned 30 false
rc=0
=== prune: apply over mixed rows
--- stdout
Abandoned lane pruning — tags older than 30 day(s)
======================================================
MODE: APPLY — the REMOVE rows below are being deleted.

REMOVE abandoned/T-1-20260901                                 48d  tag contains every branch commit
       branch:   lane/1/T-1
       worktree: <SB>/lanes/1
KEEP   abandoned/T-2-20260915                                 34d  branch moved past its tag
       branch:   lane/2/T-2
KEEP   abandoned/T-3-a-very-long-tag-name-that-overflows-the-column-20261001   18d  younger than the cut-off
REMOVE abandoned/T-4-20260801                                 79d  no branch left
       worktree: <SB>/lanes/with space
WEIRD  abandoned/T-5                                           ?d  unreadable age

------------------------------------------------------
2 removable, 2 kept.
A branch is deleted only when the abandoned TAG still contains every
commit on it. A branch that moved past its tag is live work and is left
alone; an untagged branch is never touched at all.
--- stderr
CALL lane_prune_abandoned 30 true
rc=0
=== prune: the defaults (14 days, dry)
--- stdout
Abandoned lane pruning — tags older than 14 day(s)
======================================================
MODE: DRY RUN — nothing is deleted. Re-run with --yes to apply.

REMOVE abandoned/T-1-20260901                                 48d  tag contains every branch commit
       branch:   lane/1/T-1
       worktree: <SB>/lanes/1
KEEP   abandoned/T-2-20260915                                 34d  branch moved past its tag
       branch:   lane/2/T-2
KEEP   abandoned/T-3-a-very-long-tag-name-that-overflows-the-column-20261001   18d  younger than the cut-off
REMOVE abandoned/T-4-20260801                                 79d  no branch left
       worktree: <SB>/lanes/with space
WEIRD  abandoned/T-5                                           ?d  unreadable age

------------------------------------------------------
2 removable, 2 kept.
A branch is deleted only when the abandoned TAG still contains every
commit on it. A branch that moved past its tag is live work and is left
alone; an untagged branch is never touched at all.

To apply: orchestrate.sh --prune-abandoned 14 --yes
--- stderr
CALL lane_prune_abandoned 14 false
rc=0
=== prune: nothing removable
--- stdout
Abandoned lane pruning — tags older than 14 day(s)
======================================================
MODE: DRY RUN — nothing is deleted. Re-run with --yes to apply.

KEEP   abandoned/T-9                                           3d  younger than the cut-off
       branch:   lane/9/T-9

------------------------------------------------------
0 removable, 1 kept.
A branch is deleted only when the abandoned TAG still contains every
commit on it. A branch that moved past its tag is live work and is left
alone; an untagged branch is never touched at all.
--- stderr
CALL lane_prune_abandoned 14 false
rc=0
=== prune: no tags at all (dry)
--- stdout
Abandoned lane pruning — tags older than 7 day(s)
======================================================
MODE: DRY RUN — nothing is deleted. Re-run with --yes to apply.


------------------------------------------------------
0 removable, 0 kept.
A branch is deleted only when the abandoned TAG still contains every
commit on it. A branch that moved past its tag is live work and is left
alone; an untagged branch is never touched at all.
--- stderr
CALL lane_prune_abandoned 7 false
rc=0
=== prune: no tags at all (apply)
--- stdout
Abandoned lane pruning — tags older than 7 day(s)
======================================================
MODE: APPLY — the REMOVE rows below are being deleted.


------------------------------------------------------
0 removable, 0 kept.
A branch is deleted only when the abandoned TAG still contains every
commit on it. A branch that moved past its tag is live work and is left
alone; an untagged branch is never touched at all.
--- stderr
CALL lane_prune_abandoned 7 true
rc=0
=== prune: apply is only the word true
--- stdout
Abandoned lane pruning — tags older than 30 day(s)
======================================================
MODE: DRY RUN — nothing is deleted. Re-run with --yes to apply.

REMOVE abandoned/T-1-20260901                                 48d  tag contains every branch commit
       branch:   lane/1/T-1
       worktree: <SB>/lanes/1
KEEP   abandoned/T-2-20260915                                 34d  branch moved past its tag
       branch:   lane/2/T-2
KEEP   abandoned/T-3-a-very-long-tag-name-that-overflows-the-column-20261001   18d  younger than the cut-off
REMOVE abandoned/T-4-20260801                                 79d  no branch left
       worktree: <SB>/lanes/with space
WEIRD  abandoned/T-5                                           ?d  unreadable age

------------------------------------------------------
2 removable, 2 kept.
A branch is deleted only when the abandoned TAG still contains every
commit on it. A branch that moved past its tag is live work and is left
alone; an untagged branch is never touched at all.

To apply: orchestrate.sh --prune-abandoned 30 --yes
--- stderr
CALL lane_prune_abandoned 30 yes
rc=0
=== prune: the planner fails part-way
--- stdout
Abandoned lane pruning — tags older than 30 day(s)
======================================================
MODE: DRY RUN — nothing is deleted. Re-run with --yes to apply.

REMOVE abandoned/T-1                                          40d  partial

------------------------------------------------------
1 removable, 0 kept.
A branch is deleted only when the abandoned TAG still contains every
commit on it. A branch that moved past its tag is live work and is left
alone; an untagged branch is never touched at all.

To apply: orchestrate.sh --prune-abandoned 30 --yes
--- stderr
CALL lane_prune_abandoned 30 false
rc=0
=== prune: a last row without a newline
--- stdout
Abandoned lane pruning — tags older than 30 day(s)
======================================================
MODE: DRY RUN — nothing is deleted. Re-run with --yes to apply.


------------------------------------------------------
0 removable, 0 kept.
A branch is deleted only when the abandoned TAG still contains every
commit on it. A branch that moved past its tag is live work and is left
alone; an untagged branch is never touched at all.
--- stderr
CALL lane_prune_abandoned 30 false
rc=0
=== preflight: both present and writable
--- stdout
--- stderr
rc=0
=== preflight: state missing (fatal)
--- stdout
--- stderr
ERROR: paths.state points at 'no-state', which does not exist.
ERROR:   The orchestrator writes queue/metrics/blockers there every run — fix paths.state or scaffold it.
rc=1
=== preflight: stories missing (a warning)
--- stdout
--- stderr
ERROR: ! paths.stories ('no-stories') does not exist — story tasks and the QA gate will not resolve until it does.
rc=0
=== preflight: both missing
--- stdout
--- stderr
ERROR: paths.state points at 'no-state', which does not exist.
ERROR:   The orchestrator writes queue/metrics/blockers there every run — fix paths.state or scaffold it.
ERROR: ! paths.stories ('no-stories') does not exist — story tasks and the QA gate will not resolve until it does.
rc=1
=== preflight: state is a file
--- stdout
--- stderr
ERROR: paths.state points at 'a-file', which does not exist.
ERROR:   The orchestrator writes queue/metrics/blockers there every run — fix paths.state or scaffold it.
rc=1
=== preflight: a path outside both roots
--- stdout
--- stderr
ERROR: paths.state points at '/nonexistent-elsewhere/state', which does not exist.
ERROR:   The orchestrator writes queue/metrics/blockers there every run — fix paths.state or scaffold it.
rc=1
=== preflight: a path with a space
--- stdout
--- stderr
rc=0
=== preflight: a path with a colon
--- stdout
--- stderr
ERROR: paths.state points at 'no:state', which does not exist.
ERROR:   The orchestrator writes queue/metrics/blockers there every run — fix paths.state or scaffold it.
rc=1
=== preflight: set -u, STATE_DIR / STORIES_DIR unset
--- stdout
--- stderr
<where>: STATE_DIR: unbound variable
rc=1
