📐

SPEC-STV-06-Template-System

📜

SPEC-STV-06 · Spec header. Spec ID: SPEC-STV-06 · Title: Template System · Version: 1.0.0 · Status: Planned · Authority: Specification · Priority: P1 · Owner role: Frontend lead · Reviewers: Backend architect, Product architect · Last reviewed: 2026-05-11 · Sync targets: app/Services/Templates/**, docs/TEMPLATE_SYSTEM.md · Depends on: SPEC-STV-HUB, SPEC-STV-04 · Consumed by: SPEC-STV-03, SPEC-STV-10 · Conflict rule: Hub wins. · Change policy: Frontend lead + 1 reviewer; Registry bump.

1 · Categories (seeded)

  1. Project documentation
  1. API documentation
  1. Product spec
  1. Meeting notes
  1. Company wiki
  1. SOP
  1. Roadmap
  1. Task board
  1. Knowledge base
  1. Client documentation
  1. Mobile app spec
  1. Laravel project spec
  1. AI project spec

2 · Payload schema

A template's payload JSON is a serialized page tree:

{
  "page": {
    "title": "Product Spec",
    "icon": "📐",
    "cover_url": null,
    "seo": null
  },
  "blocks": [
    { "type": "heading_1", "content": { "text": [{"text":"Overview"}] } },
    { "type": "callout", "content": { "text": [...], "icon":"🎯", "color":"blue_bg" }, "children": [...] },
    ...
  ],
  "sub_pages": [
    { "page": {...}, "blocks": [...], "sub_pages": [...] }
  ],
  "databases": [
    {
      "name": "Roadmap items",
      "properties": [
        { "name": "Status", "type": "status", "config": {...} },
        { "name": "Due",    "type": "date",   "config": {} }
      ],
      "views": [{ "name": "Board", "type": "board", "config": {...} }],
      "seed_rows": [...]
    }
  ]
}

3 · Instantiate flow

  1. User picks a template (filter by category, preview image).
  1. POST /templates/{id}/instantiate with { parent_page_uuid?, workspace_uuid }.
  1. TemplateService::instantiate() creates the page tree depth-first inside a transaction, then queues a IndexRagSourceJob per new page.
  1. Returns the root page uuid + a deep link.

4 · Save-as-template flow

  1. User opens page menu → "Save as template".
  1. POST /pages/{uuid}/save_as_template with { name, category, scope: "workspace|global" } (global is admin-only).
  1. TemplateService::serialize($page) walks the page subtree and produces the payload. Files referenced by blocks are NOT duplicated — they remain in files and are referenced by uuid; instantiation re-references the same files.

5 · Preview

GET /templates/{id}/preview returns a read-only render of the payload. The preview surface respects mobile breakpoints.

6 · Workspace vs global

  • workspace_id = null → global template, visible to every workspace.
  • workspace_id = X → only visible inside that workspace.
  • Only admins (workspace-level) can save a workspace template. Only system admins can save a global template.

7 · Versioning

A template is content-addressed by sha256(payload). Editing a template creates a new row (immutable history); old instantiations are not retroactively updated.

8 · Seeds

A php artisan templates:seed command loads the 13 starter templates from database/seeders/templates/*.json so a fresh install ships with the full library.