v1.24.0 ·
The refusal was working as designed
The roadmap projection onto GitHub Issues shipped working, and then shipped a second time with better issue bodies. Neither time was it findable.
It is opt-in and inert until a roadmap.sync section exists in the config. Nothing writes that section. The setup wizard writes six other things and not this one, no health check adds it, and the config reader only reads. The only way in was to already know the command, run it, and read the refusal.
That design is deliberate. Absence reads as "not configured", not as an error, so a project that never opted in cannot break an unrelated command and a version bump cannot start publishing someone's backlog. Those properties are worth more than discoverability.
On 7 September somebody ran the sync command in a project with no config section, read the refusal, and filed it as a possible bug. It was not one. The message named the file, said the section was absent, and stopped. It still left the reader unable to tell whether they were meant to write the section or something had failed to create it. The message answered "what is missing?" when the reader was asking "whose job is it to put it there?"
What changed
The setup wizard has a new last question: project this roadmap onto GitHub Issues? It defaults to no, and no ends it. Cancelling aborts the whole setup rather than counting as a polite decline, which is a bug this project shipped once in the compliance step.
Say yes and a second prompt lists all five roadmap stages, every one checked: inbox, backlog, in progress, done, on hold. You uncheck what you want hidden. Done cannot be unchecked, because the config reader refuses a list without it, and without the closing step issues for finished work stay open forever. The two riskier stages carry hints saying in words what publishing them means, and a test pins those hints.
The choice to check all five was the hard one. The safer default preselects three, the committed work. I decided against it. A wizard that quietly narrows the list never teaches anyone the other two are choices, and it decides for the user which of their own stages are too embarrassing to show. A project has full visibility of its own roadmap. That only holds because the list is shown in full, which makes the prompt wording the one thing between an unread question and a published backlog.
I weighed two cheaper options. A better refusal alone would have answered the September question in one line, and it shipped anyway: the refusal now names the resolved absolute path of the file it read. But it helps nobody who never runs the command. An --init flag costs other projects nothing, and loses for the same reason. A question in setup is the only one that reaches someone who does not know the feature exists. Every project pays for it, even those that answer no. Still, this project holds lifecycle machinery to a high bar because every gate is paid for by all later work, and a question is not a gate. It adds no phase, no artifact and no check. It comes last, defaults to no, and ends on no.
Details that matter
Re-running setup cannot delete a configuration you have. A project never asked keeps its section. An existing section pre-answers yes. A section that omits the stage list pre-checks the three shipped defaults, not all five, so pressing Enter through an update cannot start publishing an inbox nobody meant to publish. That constant is unchanged, since it governs hand-written configs whose authors were never asked.
The GitHub CLI is checked after you answer yes, and a missing one warns instead of blocking. Initializing on a laptop while the projection runs in CI is normal. The section is written anyway, and the sync command runs the same probe and refuses with the same message when it matters.
The health check for the resulting dead configuration only reports. A fix that applies under --yes would delete a working project's config on its first unattended run, since CI is where the GitHub CLI is legitimately absent. It also checks only that the binary exists, never authentication, because that can reach the network.
What went sideways
An earlier test asserted this projection adds no health check. I amended it, and the replacement asserts more than the count it replaced. The health check's own docs said it spawns no subprocess, but my first version called a helper that spawns git, and every new test passed. A test about terminal prompts, which runs the real CLI, timed out. The answer was written one file away.