Work Template Catalog — Task Checklist
Status: Draft v1 · Date: 2026-07-18 · Spec: spec.md · Plan: plan.md
Legend: [ ] todo · [~] in progress · [x] done. Each task names the concrete file(s).
P1 — Catalog service + endpoint + manifest consume
Repo ever-works/works
- P1-R1 Add
manifest.json—version:1,generatedBy,blueprints[]:directory,directory-minimal,marketing-site(production) +company,store(status:"placeholder",template.repo:null); onedefault:trueperchipType. - P1-R2 Add
schema/works-manifest.schema.json(draft 2020-12;required:[slug,name,title,summary,kind,chipType,status,template];template.repopattern^ever-works/[a-z0-9-]+$). - P1-R3 Add
scripts/build-manifest.mjs+.github/workflows/validate.yml(schema +default-uniqueness-per-chipTypegate).
API (apps/api)
- P1-A1
apps/api/src/works/works-template-catalog.service.ts— cloneagent-template-catalog.service.ts; reuseSAFE_SLUG_RE/stripHtml/kebabToPascal/asStringArray/MAX_*; tokenlessraw.githubusercontent.comread + GitFacade token fallback;EVER_WORKS_WORKS_REF(defaultmain) + mutable-ref warning; 1hcache_entriescache; dropplaceholder/bad-slug/bad-repo rows; map toWorkBlueprintEntry;list(chipType?). - P1-A2
apps/api/src/works/work-templates.controller.ts—WorkTemplatesController,@Controller('api/work-templates'),@Get() @Public() @HttpCode(200),@Query('chipType'),@ApiTags('Works'). - P1-A3
apps/api/src/works/works.module.ts— add service toproviders, controller tocontrollers; confirmGitFacadeService+CACHE_MANAGERin scope. - P1-A4
works-template-catalog.service.spec.ts— clone the agent spec: failure→[], malformed→[], HTML stripped, bad slug/repo dropped,chipTypefilter, cache hit, ref warning.
Web (apps/web)
- P1-W1
apps/web/src/lib/api/work-templates.ts—WorkBlueprintEntrytype + isomorphiclistBuiltinWorkBlueprints(chipType?)fallback (classic/minimal as blueprints). - P1-W2
apps/web/src/lib/api/work-templates.server.ts(server-only) —fetchWorkTemplateCatalog(chipType?)→serverFetch+ fallback. Cloneagent-templates.server.ts.
P2 — Chips + selector + rename on Create-Work-Manually
- P2-1 i18n: add
dashboard.templateSelector.label="Template"+searchPlaceholdertoapps/web/messages/en.json+ all locale files. - P2-2
WebsiteTemplateSelector.tsx— defaultlabel→t('label'); no test-id/prop change. - P2-3 chip-value →
chipTypelookup map besideWORK_KIND_ORDER(new-work-client.tsx). - P2-4 Filter blueprints by
chipType(effectiveKind); render the selector line only when the filtered list is non-empty. - P2-5
WebsiteTemplateSelector.tsx— replace native<Select>picker with a shadcnCommand-in-Popovercombobox (search onname/tags/category,featuredfirst); preserve status card + origin badge +resolveWebsiteTemplateSelection+data-testids;value=slug unchanged. - P2-6
/works/new/page.tsx— fetchfetchWorkTemplateCatalog(); pass tonew-work-client.tsx→WorkAICreator. - P2-7
WorkAICreator.tsx— feed chip-filtered list to the selector; pre-fill provider/organization selectors fromblueprint.defaults; keep bindingformData.websiteTemplateId. - P2-8 Backend create mapping — upsert-on-validate: when
websiteTemplateIdis a blueprint slug absent fromtemplates, insertbuilt_inrowworks-blueprint:<slug>(kindwebsite, blueprint repo coords + ref) sogetVisibleTemplateForUser→resolveForWork→duplicate()forks the right repo. Files:work-lifecycle.service.tsresolveValidatedWebsiteTemplateSelection,works-template-catalog.service.ts(upsert helper),template-catalog.service.ts(reuse upsert). - P2-9 API e2e —
GET /api/work-templates(+?chipType=) shape/[]; create-from-blueprint asserts theworks-blueprint:<slug>row +work.websiteTemplateId.
P3 — /new chips + custom merge + search-at-scale
- P3-1
NewPageClient.tsx— second, smaller template-chip line below the type chips, filtered bychipType, only when ≥1 blueprint; reusefetchWorkTemplateCatalogfrom/new/page.tsx. - P3-2
/newsubmit — carry chosen blueprint slug as?template=<slug>intoCHIP_TO_CANVAS_ROUTEalongside?mode=ai&kind=<workKind>(reuseinitialTemplateIdplumbing). - P3-3 Custom-first merge —
[...customRows, ...repoBlueprints]deduped by id in both Create-Work surfaces; custom rows fromlistTemplatesForUser('work'|'website', userId); group "Your templates" / "Blueprints". - P3-4 Default preselection — honor
UserTemplatePreference→ blueprintdefault:true→classic. - P3-5 Search-at-scale — autocomplete grouping,
featuredpinning, tag/category facets, dedupe + stable ordering, empty/loading states; verify against a large synthetic manifest. - P3-6 Web e2e (PROD-web harness, environment-adaptive) — selector hidden for zero-blueprint chip; searchable for
directory; custom template first;/newhandoff carries?template=; fallback path when catalog is[].
Cross-cutting / gates
- G1
pnpm type-check(apps/web**/*.tscovers e2e) +pnpm lintgreen before any branch e2e dispatch. - G2 Prettier: tabs width 4, single quotes, no trailing commas, kebab-case files.
- G3 Every failure path returns today's behavior (empty/unreachable manifest → classic/minimal via DB catalog) — assert in tests.
- G4 No entity/column/route/test-id removed or renamed; only the
templateSelector.labeli18n value changes. - G5 Pin
EVER_WORKS_WORKS_REFto a SHA/tag in the prod env before turning the manifest on.