Create missing storage directory and allow quitting early during project creation - #48
Create missing storage directory and allow quitting early during project creation#48dmccoystephenson wants to merge 4 commits into
Conversation
writeJson handed the configured path straight to ObjectMapper, which fails with a FileNotFoundException when the parent directory does not exist. Any storage location other than the working directory therefore required the user to create the directory by hand first. The parent directory is now created before the write. mkdirs() returns false both when the directory already exists and when creation genuinely failed, so an isDirectory() check distinguishes the two and only a real failure raises. Closes #32 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Once `create` started prompting there was no way out short of killing the shell: every prompt insisted on an answer, and an exhausted input stream (Ctrl-D) left the score loops re-prompting forever because a null answer fails to parse and is retried. Interactive answers now go through a readInput helper that treats `q`, `quit` (either case, surrounding whitespace ignored) and the end of the input stream as a request to stop, unwinding to a single "Project creation cancelled." message with nothing persisted. A one-line hint is printed before the first prompt, since the prompt text itself is user-configurable and cannot advertise this. The five identical score-retry loops collapse into promptForScore so the cancellation path is threaded through one place rather than five. Closes #37 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… answers Two problems surfaced while reviewing the quit-early change. The README now promises that Ctrl-D cancels creation, but that only held when a real console was attached. The no-console fallback used by IDEs and by a piped stdin calls Scanner.nextLine(), which throws NoSuchElementException on an exhausted stream rather than returning null, so exhausted input escaped as an unhandled exception instead of a clean cancellation. The fallback now reports EOF as null, matching what Console.readLine() returns, and gains its first test class. readInput also returned the trimmed answer, quietly changing how every existing answer was interpreted. Trimming is now confined to the quit comparison and the answer itself is returned untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Self-review rubric
Findings raised and resolved during this reviewTwo problems were found in the first two commits and fixed in
Judgment calls left visible for a human
This review was performed and posted during a Gardener session (https://github.com/Stephenson-Software/gardener). drafted by Claude on behalf of Daniel Stephenson |
Merge held for human reviewThis PR is complete and its gates are satisfied, but autonomous merge is withheld because one protected condition is matched. Matched condition: a single file with more than 50 deleted lines. Everything else passed:
No other protected path is touched: Merging is left to a human review of that one file. This comment was written during a Gardener session (https://github.com/Stephenson-Software/gardener). drafted by Claude on behalf of Daniel Stephenson |
Summary
Two rough edges in the project-creation path are addressed.
writeJsonpassed the configured path straight to Jackson, so aprojects.filepointing anywhere other than an existing directory failed withFileNotFoundException. The parent directory is now created before the write. Becausemkdirs()returnsfalseboth when the directory already exists and when creation genuinely failed, anisDirectory()check distinguishes the two so only a real failure raises. This also removes the prerequisite that would otherwise block Save data to user's home directory by default #33 (defaulting storage to the user's home directory).createbegan prompting, no exit existed short of killing the shell, and an exhausted input stream (Ctrl-D) left the score loops re-prompting forever, since anullanswer fails to parse and is retried. Interactive answers now pass through areadInputhelper that treatsq,quit(either case, surrounding whitespace ignored) and end-of-input as a request to stop; creation unwinds to a singleProject creation cancelled.message with nothing persisted.application.yamland is user-configurable, so it cannot be relied on to advertise the option.application.yamlis deliberately left untouched.promptForScorehelper, so the cancellation path is threaded through one place rather than five. This refactor is included because duplicating the new control flow five times was the alternative, not as independent cleanup.Test plan
./gradlew test— 94 tests, 0 failures (90 before this branch; 4 added)writeJson_WhenParentDirectoryIsMissing_ShouldCreateIt,testQuittingAtNamePromptCancelsCreationandtestEndOfInputCancelsCreationall failed;testQuittingAtScorePromptCancelsCreationnever terminated (the pre-fix re-prompt loop), and completes in milliseconds with the fix restored.README.mdupdated to document the cancel behaviour;CONTRIBUTING.mdre-checked and still accurate.Deferred backlog
The remaining open issues were not selected this cycle, with reasons recorded here for auditability:
src/main/resources/application.yamlor add packaging, paths held back from autonomous merge; Save data to user's home directory by default #33 is also unblocked by this PR and is a natural follow-up.Closes #32
Closes #37
This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).
drafted by Claude on behalf of Daniel Stephenson