Skip to content

Added recursive layout detection#816

Merged
sagzy merged 3 commits into
mainfrom
fix/recursive-template
Jun 10, 2026
Merged

Added recursive layout detection#816
sagzy merged 3 commits into
mainfrom
fix/recursive-template

Conversation

@sagzy

@sagzy sagzy commented May 21, 2026

Copy link
Copy Markdown
Contributor

ref https://linear.app/ghost/issue/BER-3133

Problem

Users have caused site outages by introducing infinite recursion in their custom theme templates (specifically, using {{!< default}} in default.hbs).

Solution

Added a v6-only GS130-NO-RECURSIVE-LAYOUT rule to catch recursive {{!< ...}} template inheritance.

Error message:

CleanShot 2026-06-04 at 13 43 21@2x

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab4139f6-bdd9-4050-9714-33ec2c3741f3

📥 Commits

Reviewing files that changed from the base of the PR and between 4ebe6a1 and 5e34852.

📒 Files selected for processing (27)
  • lib/checks/130-template-inheritance.js
  • lib/specs/v6.js
  • test/130-template-inheritance.test.js
  • test/checker.test.js
  • test/fixtures/themes/130-template-inheritance/absolute-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/base.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/default.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/empty.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/index.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/base.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/layouts/base.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/layout.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/layout.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/shell.hbs
  • test/fixtures/themes/130-template-inheritance/recursive-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/relative-explicit-extension/index.hbs
  • test/fixtures/themes/130-template-inheritance/relative-explicit-extension/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/secondary-directive/default.hbs
  • test/fixtures/themes/130-template-inheritance/secondary-directive/index.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/a.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/c.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/shared.hbs
  • test/fixtures/themes/130-template-inheritance/valid-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/valid-default/index.hbs
✅ Files skipped from review due to trivial changes (16)
  • test/fixtures/themes/130-template-inheritance/nested-default-target/default.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/c.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/layout.hbs
  • test/fixtures/themes/130-template-inheritance/recursive-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/shell.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/a.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/base.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/index.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/default.hbs
  • test/fixtures/themes/130-template-inheritance/secondary-directive/default.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/empty.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/absolute-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/base.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/layout.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/layouts/base.hbs
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/fixtures/themes/130-template-inheritance/valid-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/valid-default/index.hbs
  • lib/specs/v6.js

Walkthrough

This pull request introduces GS130-NO-RECURSIVE-LAYOUT, a new Ghost theme validation check that detects and prevents recursive template layout inheritance. The implementation includes a 154-line check module that extracts Handlebars layout directives from .hbs template files, normalizes paths, builds a directed inheritance graph, and uses DFS traversal with cycle detection to identify recursive cycles. A new v6 spec rule definition marks the violation as a fatal error. The PR adds comprehensive test fixtures modeling nine inheritance scenarios (direct cycles, indirect cycles, valid chains, path resolution edge cases, and empty/missing templates) and a 119-line test suite validating all behaviors. Integration test expectations are updated to reflect the new passing check count.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Added recursive layout detection' directly and accurately summarizes the main change: a new v6 rule to detect recursive template inheritance.
Description check ✅ Passed The description is clearly related to the changeset, explaining the problem (infinite recursion in themes), the solution (new GS130-NO-RECURSIVE-LAYOUT rule), and showing the expected error message.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/recursive-template

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.

@sagzy sagzy force-pushed the fix/recursive-template branch from 85debbc to 4ebe6a1 Compare May 21, 2026 21:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/130-template-inheritance.test.js (1)

1-46: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace should assertions with Vitest expect in test/130-template-inheritance.test.js.

This file uses require('should') and output.should... / failure.message.should... assertions; update them to vitest’s expect(...) style per the test guidelines.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/130-template-inheritance.test.js` around lines 1 - 46, Update the test
file to replace all "should" style assertions with Vitest "expect" assertions:
remove the require('should') line, import Vitest's expect if not global, and
change checks on output and results to expect(output).toBeValidThemeObject() (or
the appropriate custom matcher),
expect(Object.keys(output.results.fail)).toEqual([ruleCode]),
expect(output.results.fail).toEqual({}) /
expect(output.results.pass).toContain(ruleCode) as applicable, and change
failure.message.should.containEql(...) to expect(failure.message).toContain(...)
and failure.ref.should.eql('default.hbs') to
expect(failure.ref).toEqual('default.hbs'); update all uses of thisCheck,
output, failure, and ruleCode accordingly so assertions use expect(...) with
toEqual/toContain/toBe... matchers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@AGENTS.md`:
- Around line 105-114: Update the examples under "Common Test Patterns" to use
vitest's global expect assertions instead of the legacy should style: replace
the `check(...)` usage examples so that the passing test asserts the results
array with expect(theme.results.pass).toContain('GS010-PJ-REQ') (using check and
themePath to locate the theme) and the failing test asserts the specific failure
key with expect(theme.results.fail['GS010-PJ-NAME-REQ']).toBeDefined() or
toBeTruthy(); keep using the same helpers (`check`, `themePath`) and the same
result properties (`theme.results.pass`, `theme.results.fail`) but swap out
`.should.*` expressions for the equivalent `expect(...).toContain` /
`expect(...).toBeDefined` style.
- Around line 162-177: The example exported check function checkSomething is
missing the required third parameter themePath; update its signature to accept
(theme, options, themePath) and ensure it returns a Promise (make it async if
needed), and if options or themePath are unused add the appropriate
eslint-disable comment as noted in the guidelines so the linter won't complain.
- Around line 193-204: Update the test example in the
`test/XXX-check-name.test.js` snippet to use vitest's expect-style assertions
instead of the deprecated should library: keep the existing test structure and
the call to utils.testCheck(checkSomething, 'XXX-check-name/failing-case'), but
replace the old "output.results.fail['GSXXX-ERROR-CODE'].should.exist()"
assertion with a vitest expect assertion (e.g.,
expect(output.results.fail['GSXXX-ERROR-CODE']).toBeDefined() or another
appropriate expect matcher) so it matches the project's vitest examples and
style.

In `@lib/checks/130-template-inheritance.js`:
- Around line 137-154: The exported check function checkTemplateInheritance
currently is a synchronous two-arg function (checkTemplateInheritance(theme,
options)) but must follow the check-module contract: change it to an async
function with signature async function checkTemplateInheritance(theme, options,
themePath) (or equivalent arrow) so it returns a Promise that resolves to theme;
keep the existing logic intact, accept the extra themePath parameter (even if
unused) and ensure the module still exports checkTemplateInheritance so callers
receive a Promise.

---

Outside diff comments:
In `@test/130-template-inheritance.test.js`:
- Around line 1-46: Update the test file to replace all "should" style
assertions with Vitest "expect" assertions: remove the require('should') line,
import Vitest's expect if not global, and change checks on output and results to
expect(output).toBeValidThemeObject() (or the appropriate custom matcher),
expect(Object.keys(output.results.fail)).toEqual([ruleCode]),
expect(output.results.fail).toEqual({}) /
expect(output.results.pass).toContain(ruleCode) as applicable, and change
failure.message.should.containEql(...) to expect(failure.message).toContain(...)
and failure.ref.should.eql('default.hbs') to
expect(failure.ref).toEqual('default.hbs'); update all uses of thisCheck,
output, failure, and ruleCode accordingly so assertions use expect(...) with
toEqual/toContain/toBe... matchers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f4b0a8b8-a43b-4a7c-bcec-3e8457482a6e

📥 Commits

Reviewing files that changed from the base of the PR and between b3bec0f and 85debbc.

📒 Files selected for processing (10)
  • AGENTS.md
  • lib/checks/130-template-inheritance.js
  • lib/specs/v6.js
  • test/130-template-inheritance.test.js
  • test/checker.test.js
  • test/fixtures/themes/130-template-inheritance/indirect-cycle/default.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-cycle/layouts/base.hbs
  • test/fixtures/themes/130-template-inheritance/recursive-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/valid-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/valid-default/index.hbs

Comment thread AGENTS.md Outdated
Comment thread AGENTS.md Outdated
Comment thread AGENTS.md Outdated
Comment thread lib/checks/130-template-inheritance.js

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

Copy link
Copy Markdown

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: 85debbc2a6

ℹ️ 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 thread lib/checks/130-template-inheritance.js Outdated
@sagzy sagzy force-pushed the fix/recursive-template branch from 4ebe6a1 to 59acb95 Compare May 21, 2026 21:15
@sagzy sagzy changed the title Add v6 recursive layout detection to gscan Added recursive layout detection to v6 May 21, 2026
@sagzy sagzy changed the title Added recursive layout detection to v6 Added recursive layout detection Jun 3, 2026
return `${layoutPath}.hbs`;
}

function resolveLayoutPath(sourceFile, layoutName) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One possible issue here:

Ghost uses express-hbs without configuring layoutsDir, so declared layouts resolve relative to the file declaring them, not relative to the theme root.

For example:

theme/
├── default.hbs
└── members/
    ├── base.hbs
    └── default.hbs

If members/base.hbs contains:

{{!< default}}

Ghost resolves that to:

members/default.hbs

The current implementation appears to resolve it to:

default.hbs

So if there's a theme set up this way we might end up with false positives. However, i had an agent try to find a ghost theme that has a structure like this and (it claimed to) check like 450 of them and didn't find this in the wild lol. So it might not be a real issue.

I don't think it'd happen with default.hbs either, but maybe if someone built a theme with a bunch of layout.hbs at every level or something? i'm not sure.

I think this will fix it and the tests might explain the issue a little better, though i'm not 100% sure if i'm missing something.

I made it as a PR into your branch here, so if you like it you can add it, if not we can merge your change as-is!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@troyciesco this is a great catch! Thanks a lot for the thorough review, I didn't expect you to open a PR with the fix and have an agent go through existing Ghost themes to double-check 🙏 🙏

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/checker.test.js`:
- Around line 10-31: The test currently removes 'testHelper' from
v6Spec.knownHelpers only on the success path, risking cross-test leakage if the
expect fails; move the cleanup that finds and splices 'testHelper' from
v6Spec.knownHelpers into the existing finally block so it always runs: inside
the finally (where delete labsEnabledHelpers.testHelper is done) also retrieve
const v6Spec = spec.get(['v6']) and remove 'testHelper' if present (using
indexOf/splice) to guarantee the knownHelpers array is restored regardless of
test outcome.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab4139f6-bdd9-4050-9714-33ec2c3741f3

📥 Commits

Reviewing files that changed from the base of the PR and between 4ebe6a1 and 5e34852.

📒 Files selected for processing (27)
  • lib/checks/130-template-inheritance.js
  • lib/specs/v6.js
  • test/130-template-inheritance.test.js
  • test/checker.test.js
  • test/fixtures/themes/130-template-inheritance/absolute-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/base.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/default.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/empty.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/index.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/base.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/layouts/base.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/layout.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/layout.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/shell.hbs
  • test/fixtures/themes/130-template-inheritance/recursive-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/relative-explicit-extension/index.hbs
  • test/fixtures/themes/130-template-inheritance/relative-explicit-extension/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/secondary-directive/default.hbs
  • test/fixtures/themes/130-template-inheritance/secondary-directive/index.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/a.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/c.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/shared.hbs
  • test/fixtures/themes/130-template-inheritance/valid-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/valid-default/index.hbs
✅ Files skipped from review due to trivial changes (16)
  • test/fixtures/themes/130-template-inheritance/nested-default-target/default.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/c.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/layout.hbs
  • test/fixtures/themes/130-template-inheritance/recursive-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/shell.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/a.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/base.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/index.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/default.hbs
  • test/fixtures/themes/130-template-inheritance/secondary-directive/default.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/empty.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/absolute-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/base.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/layout.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/layouts/base.hbs
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/fixtures/themes/130-template-inheritance/valid-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/valid-default/index.hbs
  • lib/specs/v6.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/checker.test.js`:
- Around line 10-31: The test currently removes 'testHelper' from
v6Spec.knownHelpers only on the success path, risking cross-test leakage if the
expect fails; move the cleanup that finds and splices 'testHelper' from
v6Spec.knownHelpers into the existing finally block so it always runs: inside
the finally (where delete labsEnabledHelpers.testHelper is done) also retrieve
const v6Spec = spec.get(['v6']) and remove 'testHelper' if present (using
indexOf/splice) to guarantee the knownHelpers array is restored regardless of
test outcome.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab4139f6-bdd9-4050-9714-33ec2c3741f3

📥 Commits

Reviewing files that changed from the base of the PR and between 4ebe6a1 and 5e34852.

📒 Files selected for processing (27)
  • lib/checks/130-template-inheritance.js
  • lib/specs/v6.js
  • test/130-template-inheritance.test.js
  • test/checker.test.js
  • test/fixtures/themes/130-template-inheritance/absolute-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/base.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/default.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/empty.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/index.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/base.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/layouts/base.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/layout.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/layout.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/shell.hbs
  • test/fixtures/themes/130-template-inheritance/recursive-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/relative-explicit-extension/index.hbs
  • test/fixtures/themes/130-template-inheritance/relative-explicit-extension/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/secondary-directive/default.hbs
  • test/fixtures/themes/130-template-inheritance/secondary-directive/index.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/a.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/c.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/shared.hbs
  • test/fixtures/themes/130-template-inheritance/valid-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/valid-default/index.hbs
✅ Files skipped from review due to trivial changes (16)
  • test/fixtures/themes/130-template-inheritance/nested-default-target/default.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/c.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/layout.hbs
  • test/fixtures/themes/130-template-inheritance/recursive-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/shell.hbs
  • test/fixtures/themes/130-template-inheritance/shared-layout-target/a.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/base.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/index.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/default.hbs
  • test/fixtures/themes/130-template-inheritance/secondary-directive/default.hbs
  • test/fixtures/themes/130-template-inheritance/missing-and-empty/empty.hbs
  • test/fixtures/themes/130-template-inheritance/nested-cycle/layouts/default.hbs
  • test/fixtures/themes/130-template-inheritance/absolute-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/indirect-root-cycle/base.hbs
  • test/fixtures/themes/130-template-inheritance/nested-shell-target/members/layout.hbs
  • test/fixtures/themes/130-template-inheritance/nested-default-target/layouts/base.hbs
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/fixtures/themes/130-template-inheritance/valid-default/default.hbs
  • test/fixtures/themes/130-template-inheritance/valid-default/index.hbs
  • lib/specs/v6.js
🛑 Comments failed to post (1)
test/checker.test.js (1)

10-31: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Move knownHelpers cleanup into finally to avoid cross-test state leakage.

If the expect at Line 21 fails, testHelper may remain in v6Spec.knownHelpers and affect subsequent tests.

Suggested fix
 it('adds labs-enabled helpers to knownHelpers when flag is set', async function () {
     labsEnabledHelpers.testHelper = 'testFlag';
+    const v6Spec = spec.get(['v6']);

     try {
         await check(themePath('is-empty'), {
             checkVersion: 'v6',
             labs: {testFlag: true},
             skipChecks: true
         });

-        const v6Spec = spec.get(['v6']);
         expect(v6Spec.knownHelpers).toContain('testHelper');
-
-        // Clean up the spec
-        const idx = v6Spec.knownHelpers.indexOf('testHelper');
-        if (idx > -1) {
-            v6Spec.knownHelpers.splice(idx, 1);
-        }
     } finally {
+        const idx = v6Spec.knownHelpers.indexOf('testHelper');
+        if (idx > -1) {
+            v6Spec.knownHelpers.splice(idx, 1);
+        }
         delete labsEnabledHelpers.testHelper;
     }
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    it('adds labs-enabled helpers to knownHelpers when flag is set', async function () {
        labsEnabledHelpers.testHelper = 'testFlag';
        const v6Spec = spec.get(['v6']);

        try {
            await check(themePath('is-empty'), {
                checkVersion: 'v6',
                labs: {testFlag: true},
                skipChecks: true
            });

            expect(v6Spec.knownHelpers).toContain('testHelper');
        } finally {
            const idx = v6Spec.knownHelpers.indexOf('testHelper');
            if (idx > -1) {
                v6Spec.knownHelpers.splice(idx, 1);
            }
            delete labsEnabledHelpers.testHelper;
        }
    });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/checker.test.js` around lines 10 - 31, The test currently removes
'testHelper' from v6Spec.knownHelpers only on the success path, risking
cross-test leakage if the expect fails; move the cleanup that finds and splices
'testHelper' from v6Spec.knownHelpers into the existing finally block so it
always runs: inside the finally (where delete labsEnabledHelpers.testHelper is
done) also retrieve const v6Spec = spec.get(['v6']) and remove 'testHelper' if
present (using indexOf/splice) to guarantee the knownHelpers array is restored
regardless of test outcome.

@sagzy sagzy merged commit a15fb64 into main Jun 10, 2026
6 checks passed
@sagzy sagzy deleted the fix/recursive-template branch June 10, 2026 13:59
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.

2 participants