Skip to content

1139 bug leading zeros are stripped from template filename when starting with tt0xxxx#1151

Closed
chhoumann wants to merge 9 commits intomasterfrom
1139-bug-leading-zeros-are-stripped-from-template-filename-when-starting-with-tt0xxxx
Closed

1139 bug leading zeros are stripped from template filename when starting with tt0xxxx#1151
chhoumann wants to merge 9 commits intomasterfrom
1139-bug-leading-zeros-are-stripped-from-template-filename-when-starting-with-tt0xxxx

Conversation

@chhoumann
Copy link
Owner

@chhoumann chhoumann commented Mar 14, 2026


Open with Devin

Summary by CodeRabbit

  • New Features

    • Reorganized file collision handling with improved UI for configuring template behavior when target files exist.
    • Added multi-step decision flow replacing single-step configuration.
  • Improvements

    • Enhanced documentation with clearer descriptions of file handling scenarios and naming strategies.
    • Centralized file existence policy logic for more consistent behavior.
  • Bug Fixes & Updates

    • Consolidated fragmented file handling settings into unified behavior model.
    • Improved naming strategy examples (e.g., zero-padding preservation, duplicate suffix handling).

@vercel
Copy link

vercel bot commented Mar 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
quickadd Ready Ready Preview Mar 14, 2026 2:41pm

@coderabbitai
Copy link

coderabbitai bot commented Mar 14, 2026

📝 Walkthrough

Walkthrough

This PR consolidates file existence behavior handling from legacy multiple fields into a unified policy-driven system. It introduces a new fileExistsPolicy module defining behavior categories and resolution strategies, removes legacy constants, refactors TemplateChoiceEngine with modular helpers, and adds migrations to normalize existing configurations.

Changes

Cohort / File(s) Summary
Documentation
docs/docs/Choices/TemplateChoice.md, docs/docs/Examples/Template_CreateMOCNoteWithLinkDashboard.md
Rewrote file-existence behavior descriptions with structured decision flow and added examples for increment and duplicate-suffix naming strategies.
New File-Exists Policy System
src/template/fileExistsPolicy.ts, src/template/fileExistsPolicy.test.ts
Comprehensive new module defining behavior categories (prompt, update, create, keep), mode definitions with resolution logic, and utilities for path collision resolution (increment, duplicate-suffix). Includes 215-line test suite.
Type Updates
src/types/choices/ITemplateChoice.ts, src/types/choices/TemplateChoice.ts
Replaced separate fileExistsMode and setFileExistsBehavior fields with unified fileExistsBehavior property of type TemplateFileExistsBehavior.
Constants Removal
src/constants.ts
Removed legacy file-exists constants: fileExistsIncrement, fileExistsAppendToBottom, fileExistsAppendToTop, fileExistsOverwriteFile, fileExistsDoNothing, and fileExistsChoices array.
Engine Refactoring
src/engine/TemplateChoiceEngine.ts
Major refactor replacing inline file-collision logic with mode-driven approach using new helpers: getSelectedFileExistsMode, applyFileExistsMode, applyExistingFileUpdate. Replaced user prompts with programmatic mode resolution. (+117/-78 lines)
Removed Engine Logic
src/engine/TemplateEngine.ts
Removed protected incrementFileName helper method (−39 lines); collision path resolution now delegated to fileExistsPolicy.
New Collision Tests
src/engine/TemplateChoiceEngine.collision.test.ts
Added 419-line comprehensive test suite covering prompt behavior, increment/duplicate-suffix creation, folder collision handling, and automatic behavior scenarios.
Template Engine Tests Removed
src/engine/templateEngine-increment-canvas.test.ts
Removed 96-line test suite for incrementFileName; functionality tested via new collision tests.
Migration Infrastructure
src/migrations/consolidateFileExistsBehavior.ts, src/migrations/consolidateFileExistsBehavior.test.ts
New migration normalizing legacy split fields (fileExistsMode, setFileExistsBehavior, incrementFileName) into unified fileExistsBehavior. Handles nested macro command choices. Includes 86-line test coverage.
Migration Helpers
src/migrations/helpers/normalizeTemplateFileExistsBehavior.ts
New module providing isTemplateChoice guard, migrateFileExistsBehavior logic, and normalizeTemplateChoice mutation for legacy→modern conversion.
Legacy Migration Helpers
src/migrations/helpers/isOldTemplateChoice.ts
Removed OldTemplateChoice type and isOldTemplateChoice guard; logic inlined where needed.
Updated Legacy Migration
src/migrations/incrementFileNameSettingMoveToDefaultBehavior.ts, src/migrations/incrementFileNameSettingMoveToDefaultBehavior.test.ts
Added local OldTemplateChoice type and guard; added 99-line test suite for legacy incrementFileName→split-behavior migration path.
Migration Registry
src/migrations/migrate.ts
Added consolidateFileExistsBehavior to migrations collection.
UI Builder Refactor
src/gui/ChoiceBuilder/templateChoiceBuilder.ts
Major refactor: replaced static options with dynamic category-driven policy (getBehaviorCategory, getDefaultBehaviorForCategory, getModesForCategory). Conditional early exits for prompt/keep; dynamic second Setting for mode selection. (+58/-33 lines)
Settings
src/settings.ts
Added consolidateFileExistsBehavior boolean flag to QuickAddSettings.migrations.
Test Mock Updates
src/engine/CaptureChoiceEngine.notice.test.ts, src/engine/CaptureChoiceEngine.template-property-types.test.ts, src/engine/MacroChoiceEngine.notice.test.ts, src/engine/TemplateChoiceEngine.notice.test.ts, src/preflight/runOnePagePreflight.selection.test.ts
Updated default settings mocks to include consolidateFileExistsBehavior flag and fileExistsBehavior property; removed legacy fileExistsMode and setFileExistsBehavior references.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Possibly related issues

Possibly related PRs

Suggested labels

released


🐰 Files reorganized with methodical care,
Old constants shuffled, policies laid bare,
From split fields to unified design,
Collisions now resolve—increment or align!
The template engine hops with joy today, 🎉
As legacy logic finds a modern way!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title references issue 1139 about leading zeros being stripped from filenames starting with 'tt0xxxx', which is directly addressed by the comprehensive fileExistsPolicy module and collision handling refactor.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 1139-bug-leading-zeros-are-stripped-from-template-filename-when-starting-with-tt0xxxx
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…ros-are-stripped-from-template-filename-when-starting-with-tt0xxxx
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 14, 2026

Deploying quickadd with  Cloudflare Pages  Cloudflare Pages

Latest commit: 91c24f0
Status: ✅  Deploy successful!
Preview URL: https://ad0935d4.quickadd.pages.dev
Branch Preview URL: https://1139-bug-leading-zeros-are-s.quickadd.pages.dev

View logs

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0f8fa2e626

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +15 to +19
if (isMultiChoice(choice)) {
choice.choices = normalizeChoices(choice.choices);
}

if (isTemplateChoice(choice)) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Normalize nested template commands inside Macro choices

This migration only descends into Multi children, so Template choices embedded in Macro choice command lists are skipped. Those embedded templates can still carry legacy setFileExistsBehavior/fileExistsMode, and because TemplateChoiceEngine.getSelectedFileExistsMode falls back to { kind: "prompt" } when fileExistsBehavior is missing, users who configured a fixed collision action in nested macro template commands will start getting prompts after this update.

Useful? React with 👍 / 👎.

Comment on lines +31 to +34
for (const command of macro.commands) {
if (isNestedChoiceCommand(command) && isTemplateChoice(command.choice)) {
normalizeTemplateChoice(command.choice);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recurse through conditional macro branches in migration

normalizeMacros only inspects top-level macro.commands entries and does not walk nested command arrays (for example Conditional thenCommands/elseCommands). Any template choice nested in those branches keeps legacy collision fields, so its file-exists behavior is not migrated and will silently degrade to prompt mode at runtime.

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (6)
src/migrations/incrementFileNameSettingMoveToDefaultBehavior.test.ts (1)

4-99: Add a regression case for incrementFileName: false.

Please add a test that verifies incrementFileName: false does not force setFileExistsBehavior/fileExistsMode to increment, while still removing incrementFileName.

Based on learnings: Applies to **/*.test.{ts,tsx} : Add regression coverage for bug fixes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/migrations/incrementFileNameSettingMoveToDefaultBehavior.test.ts` around
lines 4 - 99, Add a regression test that supplies a choice with
incrementFileName: false and asserts after calling migration.migrate that (1)
setFileExistsBehavior and fileExistsMode were NOT changed to the "Increment the
file name" values for that choice, and (2) the incrementFileName property was
removed; do the same for a nested macro command choice (check
plugin.settings.choices[...] and plugin.settings.macros[0].commands[0].choice)
to ensure migration.migrate only converts true and strips the property for false
without modifying fileExistsMode or setFileExistsBehavior.
src/migrations/consolidateFileExistsBehavior.test.ts (1)

54-85: Strengthen nested-macro assertions by validating legacy field cleanup.

The nested test verifies the new field, but also asserting removal of legacy fields would better prevent partial-normalization regressions.

Suggested assertion additions
 		expect(plugin.settings.macros[0].commands[0].choice).toMatchObject({
 			fileExistsBehavior: { kind: "prompt" },
 		});
+		expect(
+			plugin.settings.macros[0].commands[0].choice.fileExistsMode,
+		).toBeUndefined();
+		expect(
+			plugin.settings.macros[0].commands[0].choice.setFileExistsBehavior,
+		).toBeUndefined();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/migrations/consolidateFileExistsBehavior.test.ts` around lines 54 - 85,
The test should not only assert the new normalized field but also verify legacy
fields are removed to prevent partial normalization; after calling
migration.migrate(plugin) add assertions that
plugin.settings.macros[0].commands[0].choice.setFileExistsBehavior and
.fileExistsMode are undefined (or not present) alongside the existing expect for
fileExistsBehavior, referencing the same nested object in plugin.settings.macros
and migration.migrate to locate the code to update.
src/migrations/consolidateFileExistsBehavior.ts (2)

49-49: Redundant deepClone call on line 49.

normalizeChoices mutates the array in-place and returns it. The input choicesCopy is already a deep clone from line 46. Wrapping the result in another deepClone is unnecessary and adds overhead.

♻️ Proposed fix
-		plugin.settings.choices = deepClone(normalizeChoices(choicesCopy));
+		plugin.settings.choices = normalizeChoices(choicesCopy);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/migrations/consolidateFileExistsBehavior.ts` at line 49, Remove the
redundant deepClone when assigning plugin.settings.choices: since choicesCopy is
already produced by deepClone and normalizeChoices mutates and returns that
array, replace plugin.settings.choices =
deepClone(normalizeChoices(choicesCopy)) with a direct assignment
plugin.settings.choices = normalizeChoices(choicesCopy) (referencing
normalizeChoices, choicesCopy, and plugin.settings.choices).

47-47: Type assertion for macros access.

Using (plugin.settings as any).macros suggests macros may not be in the typed settings interface. This is acceptable for migration code dealing with legacy data structures, but consider adding a comment explaining why the assertion is needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/migrations/consolidateFileExistsBehavior.ts` at line 47, The cast
(plugin.settings as any).macros is used to read legacy/unknown migration data
but lacks explanation; add a concise comment above the line that explains this
is migration code handling legacy settings where macros may not exist on the
typed Settings interface, so we use a type assertion to avoid TS errors while
safely defaulting to [] (line referencing deepClone and macrosCopy). Keep the
existing behavior (deepClone((plugin.settings as any).macros || [])) but
document why the assertion is required and that this is intentional for
backward-compatibility during the consolidateFileExistsBehavior migration.
src/gui/ChoiceBuilder/templateChoiceBuilder.ts (1)

471-476: Inconsistent indentation on Line 475.

The this.reload() call has an extra level of indentation compared to the surrounding code.

🧹 Proposed fix
 				dropdown.setValue(selectedMode).onChange((value: FileExistsModeId) => {
 					this.choice.fileExistsBehavior = {
 						kind: "apply",
 						mode: value,
 					};
-						this.reload();
+					this.reload();
 				});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gui/ChoiceBuilder/templateChoiceBuilder.ts` around lines 471 - 476, Align
the indentation of the this.reload() call with the surrounding block: inside the
callback where this.choice.fileExistsBehavior is set (the code that assigns kind
and mode), reduce one level of indentation so that this.reload() sits at the
same indentation level as the this.choice assignment; update the callback in
templateChoiceBuilder.ts (the block that sets choice.fileExistsBehavior) to keep
consistent formatting with the surrounding code.
src/migrations/helpers/normalizeTemplateFileExistsBehavior.ts (1)

30-35: Add warning log when fallback to "increment" mode occurs.

When mapLegacyFileExistsModeToId returns null (unknown or non-string legacy mode), the code silently defaults to "increment". This masks potential data issues and should be logged so users can diagnose migration problems.

Import the logger and add a warning:

import { logger as log } from "src/utils/Logger";

Then at line 33, log when the fallback is triggered:

const mode = mapLegacyFileExistsModeToId(choice.fileExistsMode);
if (mode === null) {
	log.logWarning(`Unknown file exists mode, defaulting to "increment": ${choice.fileExistsMode}`);
}
return {
	kind: "apply",
	mode: mode ?? "increment",
};

This pattern is already used in other migrations (e.g., migrateProviderApiKeysToSecretStorage.ts) for similar fallback scenarios.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/migrations/helpers/normalizeTemplateFileExistsBehavior.ts` around lines
30 - 35, Import the logger (import { logger as log } from "src/utils/Logger")
into normalizeTemplateFileExistsBehavior.ts, call
mapLegacyFileExistsModeToId(choice.fileExistsMode) into a local const (e.g.,
const mode = mapLegacyFileExistsModeToId(choice.fileExistsMode)), and if mode
=== null call log.logWarning with a message that includes choice.fileExistsMode
(e.g., "Unknown file exists mode, defaulting to \"increment\": <value>"); then
return the object with kind: "apply" and mode: mode ?? "increment". Ensure you
update the block that currently returns directly when
choice.setFileExistsBehavior is truthy to use this new logic and reference
mapLegacyFileExistsModeToId and choice.fileExistsMode.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/docs/Choices/TemplateChoice.md`:
- Line 76: The code spans in the "Append duplicate suffix" line contain leading
spaces inside backticks (`` ` (1)` ``, `` ` (2)` ``) which triggers MD038; edit
the "Append duplicate suffix" description to remove the leading spaces inside
the code spans so they read `(1)` and `(2)` (e.g., change `` ` (1)` `` to ``
`(1)` ``) ensuring the examples `note.md` → `note (1).md` remain correct.

In `@src/migrations/consolidateFileExistsBehavior.ts`:
- Around line 27-39: normalizeMacros currently only normalizes direct
TemplateChoice values on NestedChoiceCommand.command.choice, so TemplateChoice
items inside a MultiChoice are skipped; update normalizeMacros (and its loop
over macro.commands) to detect when command.choice is a MultiChoice and
recursively traverse its choices (like the existing normalizeChoices
implementation) calling normalizeTemplateChoice on any nested TemplateChoice
items (use isNestedChoiceCommand, isTemplateChoice, normalizeTemplateChoice, and
the MultiChoice type checks to locate and normalize nested choices).

In `@src/migrations/incrementFileNameSettingMoveToDefaultBehavior.ts`:
- Around line 16-25: isOldTemplateChoice currently only checks for the presence
of incrementFileName and the migration later always enables increment behavior;
change the guard and migration logic so increment mode is enabled only when
incrementFileName === true: update isOldTemplateChoice to verify the property
exists and is a boolean (and prefer checking (choice as { incrementFileName?:
boolean }).incrementFileName === true where used), and modify the migration
assignments that unconditionally force increment behavior to instead set
increment only when the original choice.incrementFileName === true (leave or
default to false when it's absent or explicitly false).

---

Nitpick comments:
In `@src/gui/ChoiceBuilder/templateChoiceBuilder.ts`:
- Around line 471-476: Align the indentation of the this.reload() call with the
surrounding block: inside the callback where this.choice.fileExistsBehavior is
set (the code that assigns kind and mode), reduce one level of indentation so
that this.reload() sits at the same indentation level as the this.choice
assignment; update the callback in templateChoiceBuilder.ts (the block that sets
choice.fileExistsBehavior) to keep consistent formatting with the surrounding
code.

In `@src/migrations/consolidateFileExistsBehavior.test.ts`:
- Around line 54-85: The test should not only assert the new normalized field
but also verify legacy fields are removed to prevent partial normalization;
after calling migration.migrate(plugin) add assertions that
plugin.settings.macros[0].commands[0].choice.setFileExistsBehavior and
.fileExistsMode are undefined (or not present) alongside the existing expect for
fileExistsBehavior, referencing the same nested object in plugin.settings.macros
and migration.migrate to locate the code to update.

In `@src/migrations/consolidateFileExistsBehavior.ts`:
- Line 49: Remove the redundant deepClone when assigning
plugin.settings.choices: since choicesCopy is already produced by deepClone and
normalizeChoices mutates and returns that array, replace plugin.settings.choices
= deepClone(normalizeChoices(choicesCopy)) with a direct assignment
plugin.settings.choices = normalizeChoices(choicesCopy) (referencing
normalizeChoices, choicesCopy, and plugin.settings.choices).
- Line 47: The cast (plugin.settings as any).macros is used to read
legacy/unknown migration data but lacks explanation; add a concise comment above
the line that explains this is migration code handling legacy settings where
macros may not exist on the typed Settings interface, so we use a type assertion
to avoid TS errors while safely defaulting to [] (line referencing deepClone and
macrosCopy). Keep the existing behavior (deepClone((plugin.settings as
any).macros || [])) but document why the assertion is required and that this is
intentional for backward-compatibility during the consolidateFileExistsBehavior
migration.

In `@src/migrations/helpers/normalizeTemplateFileExistsBehavior.ts`:
- Around line 30-35: Import the logger (import { logger as log } from
"src/utils/Logger") into normalizeTemplateFileExistsBehavior.ts, call
mapLegacyFileExistsModeToId(choice.fileExistsMode) into a local const (e.g.,
const mode = mapLegacyFileExistsModeToId(choice.fileExistsMode)), and if mode
=== null call log.logWarning with a message that includes choice.fileExistsMode
(e.g., "Unknown file exists mode, defaulting to \"increment\": <value>"); then
return the object with kind: "apply" and mode: mode ?? "increment". Ensure you
update the block that currently returns directly when
choice.setFileExistsBehavior is truthy to use this new logic and reference
mapLegacyFileExistsModeToId and choice.fileExistsMode.

In `@src/migrations/incrementFileNameSettingMoveToDefaultBehavior.test.ts`:
- Around line 4-99: Add a regression test that supplies a choice with
incrementFileName: false and asserts after calling migration.migrate that (1)
setFileExistsBehavior and fileExistsMode were NOT changed to the "Increment the
file name" values for that choice, and (2) the incrementFileName property was
removed; do the same for a nested macro command choice (check
plugin.settings.choices[...] and plugin.settings.macros[0].commands[0].choice)
to ensure migration.migrate only converts true and strips the property for false
without modifying fileExistsMode or setFileExistsBehavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1337bf17-d036-4883-8839-2113dafbb5d0

📥 Commits

Reviewing files that changed from the base of the PR and between 5ea1db0 and 0f8fa2e.

📒 Files selected for processing (25)
  • docs/docs/Choices/TemplateChoice.md
  • docs/docs/Examples/Template_CreateMOCNoteWithLinkDashboard.md
  • src/constants.ts
  • src/engine/CaptureChoiceEngine.notice.test.ts
  • src/engine/CaptureChoiceEngine.template-property-types.test.ts
  • src/engine/MacroChoiceEngine.notice.test.ts
  • src/engine/TemplateChoiceEngine.collision.test.ts
  • src/engine/TemplateChoiceEngine.notice.test.ts
  • src/engine/TemplateChoiceEngine.ts
  • src/engine/TemplateEngine.ts
  • src/engine/templateEngine-increment-canvas.test.ts
  • src/gui/ChoiceBuilder/templateChoiceBuilder.ts
  • src/migrations/consolidateFileExistsBehavior.test.ts
  • src/migrations/consolidateFileExistsBehavior.ts
  • src/migrations/helpers/isOldTemplateChoice.ts
  • src/migrations/helpers/normalizeTemplateFileExistsBehavior.ts
  • src/migrations/incrementFileNameSettingMoveToDefaultBehavior.test.ts
  • src/migrations/incrementFileNameSettingMoveToDefaultBehavior.ts
  • src/migrations/migrate.ts
  • src/preflight/runOnePagePreflight.selection.test.ts
  • src/settings.ts
  • src/template/fileExistsPolicy.test.ts
  • src/template/fileExistsPolicy.ts
  • src/types/choices/ITemplateChoice.ts
  • src/types/choices/TemplateChoice.ts
💤 Files with no reviewable changes (4)
  • src/engine/TemplateEngine.ts
  • src/constants.ts
  • src/engine/templateEngine-increment-canvas.test.ts
  • src/migrations/helpers/isOldTemplateChoice.ts

Comment on lines +27 to +39
function normalizeMacros(macros: IMacro[]): IMacro[] {
for (const macro of macros) {
if (!Array.isArray(macro.commands)) continue;

for (const command of macro.commands) {
if (isNestedChoiceCommand(command) && isTemplateChoice(command.choice)) {
normalizeTemplateChoice(command.choice);
}
}
}

return macros;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if MultiChoice can be nested inside macro commands by looking at types and existing data patterns
ast-grep --pattern $'interface NestedChoiceCommand {
  $$$
  choice: $_
  $$$
}'

# Also check how the previous migration handles this case
rg -n "isMultiChoice" src/migrations/

Repository: chhoumann/quickadd

Length of output: 1032


🏁 Script executed:

#!/bin/bash
# Read the consolidateFileExistsBehavior.ts file to see the full context
cat -n src/migrations/consolidateFileExistsBehavior.ts | head -60

Repository: chhoumann/quickadd

Length of output: 2061


🏁 Script executed:

#!/bin/bash
# Read incrementFileNameSettingMoveToDefaultBehavior.ts to see how it handles similar cases
cat -n src/migrations/incrementFileNameSettingMoveToDefaultBehavior.ts | head -70

Repository: chhoumann/quickadd

Length of output: 2570


🏁 Script executed:

#!/bin/bash
# Check the NestedChoiceCommand type definition
rg -A 5 "interface NestedChoiceCommand" src/

Repository: chhoumann/quickadd

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Search for NestedChoiceCommand type definition
rg -B 2 -A 10 "type.*NestedChoiceCommand|interface.*NestedChoiceCommand" src/

Repository: chhoumann/quickadd

Length of output: 10742


🏁 Script executed:

#!/bin/bash
# Also check the helper file
cat -n src/migrations/helpers/isNestedChoiceCommand.ts

Repository: chhoumann/quickadd

Length of output: 506


normalizeMacros should recursively handle MultiChoice within macro commands.

The function only checks for direct TemplateChoice values in nested choice commands, but command.choice can be a MultiChoice containing template choices. If a macro contains a NestedChoiceCommand where the choice is a MultiChoice, any TemplateChoice items nested inside won't be normalized. Add recursive MultiChoice handling similar to the normalizeChoices function (lines 13-25).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/migrations/consolidateFileExistsBehavior.ts` around lines 27 - 39,
normalizeMacros currently only normalizes direct TemplateChoice values on
NestedChoiceCommand.command.choice, so TemplateChoice items inside a MultiChoice
are skipped; update normalizeMacros (and its loop over macro.commands) to detect
when command.choice is a MultiChoice and recursively traverse its choices (like
the existing normalizeChoices implementation) calling normalizeTemplateChoice on
any nested TemplateChoice items (use isNestedChoiceCommand, isTemplateChoice,
normalizeTemplateChoice, and the MultiChoice type checks to locate and normalize
nested choices).

Comment on lines +16 to +25
function isOldTemplateChoice(
choice: unknown,
): choice is IChoice & OldTemplateChoice {
return (
typeof choice === "object" &&
choice !== null &&
"type" in choice &&
(choice as { type?: string }).type === "Template" &&
"incrementFileName" in choice
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t force increment mode when incrementFileName is false.

The current guard matches on property presence, then Lines 35-36 and 53-54 always force increment behavior. Legacy objects with incrementFileName: false would be migrated incorrectly.

Suggested patch
 function recursiveRemoveIncrementFileName(choices: IChoice[]): IChoice[] {
 	for (const choice of choices) {
 		if (isMultiChoice(choice)) {
 			choice.choices = recursiveRemoveIncrementFileName(choice.choices);
 		}

 		if (isOldTemplateChoice(choice)) {
-			choice.setFileExistsBehavior = true;
-			choice.fileExistsMode = "Increment the file name";
+			if (choice.incrementFileName === true) {
+				choice.setFileExistsBehavior = true;
+				choice.fileExistsMode = "Increment the file name";
+			}
 			delete choice.incrementFileName;
 		}
 	}
@@
 		for (const command of macro.commands) {
 			if (
 				isNestedChoiceCommand(command) &&
 				isOldTemplateChoice(command.choice)
 			) {
-				command.choice.setFileExistsBehavior = true;
-				command.choice.fileExistsMode = "Increment the file name";
+				if (command.choice.incrementFileName === true) {
+					command.choice.setFileExistsBehavior = true;
+					command.choice.fileExistsMode = "Increment the file name";
+				}
 				delete command.choice.incrementFileName;
 			}
 		}
 	}

Also applies to: 34-38, 50-56

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/migrations/incrementFileNameSettingMoveToDefaultBehavior.ts` around lines
16 - 25, isOldTemplateChoice currently only checks for the presence of
incrementFileName and the migration later always enables increment behavior;
change the guard and migration logic so increment mode is enabled only when
incrementFileName === true: update isOldTemplateChoice to verify the property
exists and is a boolean (and prefer checking (choice as { incrementFileName?:
boolean }).incrementFileName === true where used), and modify the migration
assignments that unconditionally force increment behavior to instead set
increment only when the original choice.incrementFileName === true (leave or
default to false when it's absent or explicitly false).

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

@chhoumann chhoumann closed this Mar 14, 2026
@chhoumann chhoumann deleted the 1139-bug-leading-zeros-are-stripped-from-template-filename-when-starting-with-tt0xxxx branch March 14, 2026 15:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant