Design: templates become internal to the-loop
Phase 2 of 3 (bugfix → design → tasks). Derives from the approved bugfix spec.
Overview
Move the template set out of the per-project footprint and into the plugin's skill, then repoint every reference at the internal location and teach init/upgrade about the change. Nothing about how an artifact is authored changes — only where the template is read from and what lands in a consuming repo.
Architecture
Templates were being treated as two things at once: (a) plugin-internal source that the harness reads to author artifacts, and (b) per-project managed files listed in the manifest and materialized by init. This bug is the (b) half. The fix collapses them to just (a):
${CLAUDE_PLUGIN_ROOT}/skills/the-loop/templates/ <- the single, internal home- The plugin ships one copy of the templates inside the
the-loopskill (Agent Skills bundles supporting files alongsideSKILL.md). manifest.templatesDirrecords that location so tooling resolves it from one place.manifest.deprecatedrecords.the-loop/templates/as a path older versions created, driving upgrade's cleanup.
See docs/architecture/architecture.md §1 (Distribution) — the templates bullet now lives there, not under §2 (Project footprint).
Components & interfaces
skills/the-loop/templates/— the moved template set (was.the-loop/templates/)..the-loop/manifest.yaml— drops thetemplates:per-project list; gainstemplatesDirand adeprecatedlist.commands/init.md— scaffolds files from the internal templates but no longer copies the templates directory into the project.commands/upgrade-the-loop.md— new "clean up deprecated paths" step removes.the-loop/templates/; report gains a removed (deprecated) group.- Commands / skill / reference docs — every
${CLAUDE_PLUGIN_ROOT}/.the-loop/templates/…and bare.the-loop/templates/…reference repoints toskills/the-loop/templates/…. cli/the_loop/webhook/dispatcher.py— default template path constants and the "kept in sync with" comments point at the new location; the built-inDEFAULT_PROMPT_TEMPLATE/DEFAULT_SPAWN_TEMPLATEremain the source of truth in a project repo (the path is absent there, so the dispatcher falls back — unchanged behaviour).config.schema.json/config.yaml(+ template config) —templatePath,promptTemplate,spawnPromptTemplatedefaults move toskills/the-loop/templates/…with descriptions clarifying they resolve under${CLAUDE_PLUGIN_ROOT}and are override-able.
UI/UX design
N/A — packaging / CLI / docs change with no user-facing surface.
Data models
manifest.yaml shape change only:
- removed:
templates:(list of per-project template paths). - added:
templatesDir: skills/the-loop/templates(plugin-relative). - added:
deprecated: [{ path, role, reason, removeIn }].
Error handling
The webhook dispatcher already guards a missing template file (_load_template returns the built-in default and logs at debug). Because a project repo will not contain skills/the-loop/templates/…, that fallback path is the normal case there and keeps the receiver working. Upgrade's cleanup confirms before deleting when the user has added their own files under a deprecated path.
Testing strategy
Primarily a docs/packaging change verified by inspection and the existing suite:
ruff,pyright,pyteststay green — the dispatcher change is limited to default path constants/comments; no behavioural test change (built-in fallback already covered).markdownlintstays green across the edited docs.- Manual verification:
rg "\.the-loop/templates"returns only intentional references (the deprecated-path entry in the manifest and upgrade command, plus historical per-work-item spec/decision records that are immutable deltas).
Trade-offs & decisions
- Home =
skills/the-loop/templates/(inside the skill) rather than a new top-leveltemplates/dir, matching the issue's "artifacts within skills that the-loop has" and the Agent Skills convention of bundling supporting files with the skill. - Keep the webhook/pr-briefing template files in the plugin (not only the Python constants) so they remain readable/override references; the dispatcher's built-in defaults stay the runtime source of truth.
- Data-driven cleanup via
manifest.deprecatedrather than hard-coding the path in the upgrade command, so future removals reuse the same mechanism.
Open questions
None.