Skip to content

Find the document shell wherever it lives, and ask when it can't be found - #219

Closed
ralyodio wants to merge 1 commit into
masterfrom
worktree-ad-install-finds-layouts
Closed

Find the document shell wherever it lives, and ask when it can't be found#219
ralyodio wants to merge 1 commit into
masterfrom
worktree-ad-install-finds-layouts

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Submit PR to install all sizes was ending at "No layout/template file with a </body> tag was found in the repo" — a dead end with no next step, on repos that plainly have a body to install into.

Why it missed

Discovery knew a hand-written list of framework conventions and, when those missed, fell back to GitHub code search — which does not index private repos. A shell kept anywhere else was invisible.

What changed

Read the repo tree. One request lists every path. We probe anything template-shaped: document extensions (.html, .astro, .erb, .njk, .twig, …) anywhere, and component extensions whose name or directory says "document shell". Build output, vendored code and fixtures are never opened. This works on private repos, so it runs before code search.

Cheaper, too. The tree also tells us which canonical paths exist, so a repo now costs one request per real file instead of a miss for every convention we ever wrote down — and probes run six at a time instead of serially.

Wider canonical list: SvelteKit src/app.html, Remix app/root.tsx, Rails, Django/Jinja, Eleventy, Blade, WordPress footer.php, and .js layouts.

Stopped penalizing templates/. For Django, Flask, Jinja and Rails that directory is exactly where the shell lives, and the -100 was hiding the only installable file in those repos.

When all three passes come up empty, ask. The publisher knows where their shell is. installAdEmbed returns needsTargetPath, and the slot manager prompts for a file path and retries against the same repo. The route already accepted target_path — the UI just never sent it. The same hook fires when a named path is wrong or has no </body>.

The stats-tracker installer had the identical dead end and gets the same tree fallback. It also no longer runs code search once a repo is already installed — that path could have injected a second copy into an unrelated file.

Verification

pnpm install still fails at HEAD (the @profullstack/autoblog pin), so there is no vitest or tsc in this checkout. The new logic was exercised against a fake GitHub API by bundling the unmodified source with the esbuild that is in node_modules — 33 checks, all passing:

  • a shell no convention predicts (server/render/document.tsx) is found
  • each newly-canonical shell ranks first (SvelteKit, Remix, Django, WordPress, Rails, Eleventy)
  • dist/ and node_modules/ are never offered
  • a repo with one layout costs exactly one file request
  • with the tree API unavailable, behaviour falls back to the old canonical probing
  • a truncated tree does not make us trust absence
  • rootPath still scopes discovery, with no double-prefixed probe
  • ad install into a tree-discovered shell puts every unit above </body> with one loader
  • empty discovery returns needsTargetPath; an explicit path installs; a bad explicit path asks again
  • an already-installed repo is still left alone

Contract tests covering all of the above are added to tests/contract/install-{ad,tracker}.test.ts for when the repo can install again.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LT2u8KFbyeicqE9z2kUHJR

…ound

"Submit PR to install all sizes" ended at "No layout/template file with a
</body> tag was found in the repo" — a dead end with no next step, on repos
that plainly have a body to install into.

Discovery only knew a hand-written list of conventions, and fell back to
GitHub code search, which does not index private repos. A shell anywhere
else was invisible.

- Read the repo tree (one request) and probe anything template-shaped:
  document extensions anywhere, component extensions whose name or directory
  says "shell". Build output, vendored code and fixtures are never opened.
  This works on private repos, so it runs before code search.
- The tree also says which canonical paths exist, so a repo now costs one
  request per real file instead of a miss for every convention we know.
  Probes run six at a time rather than one after another.
- Widen the canonical list: SvelteKit src/app.html, Remix app/root.tsx,
  Rails, Django/Jinja, Eleventy, Blade, WordPress footer.php, .js layouts.
- Stop penalizing templates/ — for Django, Flask, Jinja and Rails that is
  exactly where the shell lives, and the -100 hid the only installable file.
- When all three passes come up empty, say so and ask for the path: the
  publisher knows where their shell is. The ad installer returns
  needsTargetPath and the slot manager prompts for a file, then retries
  against the same repo. The route already accepted target_path.
- The stats-tracker installer had the same dead end and gets the same tree
  fallback; it also no longer runs code search once a repo is already
  installed, which could have injected a second copy elsewhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LT2u8KFbyeicqE9z2kUHJR
@ralyodio

Copy link
Copy Markdown
Contributor Author

Superseded by #220. deb65fe landed the same tree-walk idea on master while this was in flight; #220 is rebased on it and keeps only what is still missing there — the tree fallback in installTracker (which opens the PR, as opposed to the picker), the templates/ ranking fix, the basename gate for Rails/Laravel/WordPress shells, and test coverage for the walk (the mock had no listRepoTree, so it was never exercised).

@ralyodio ralyodio closed this Aug 29, 2026
@ralyodio
ralyodio deleted the worktree-ad-install-finds-layouts branch August 29, 2026 06:01
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