Templates
A template or a workspace (issue #68, BRAIN-06) is not a new kind of storage: it is an existing Root Folder whose root CLAUDE.md file (the manifest) carries three reserved tags on top of the pre-existing type=contract ICM tag -- icm-kind=template|workspace, review-status=draft| approved, and template-version=<semver>. lineage_folder_id is the only schema addition; see ICM convention vocabulary and ADR-0006 for the full design rationale. Generic tv file tag set/unset cannot mutate those three reserved tags on a root CLAUDE.md -- only the trusted RPCs behind the commands on this page can.
Commands
tv template list [--json]
tv template show <template-id|unambiguous-name> [--json]
tv template instantiate <template> --name <workspace-name> [--json]
tv template derive <template> --name <template-name> --description <text> [--json]
tv template import (<path|zip> | --starter) --name <template-name> --description <text> [--json]
tv template update <template> <path|zip> --reason <text> [--json]
tv template review <template> --approve --reason <text> [--json]
Every <template>/<template-id|unambiguous-name> argument accepts a UUID (always unambiguous) or a template's exact name. A name is resolved against tv template list's approved, visible templates; zero matches fails with TEMPLATE_NOT_FOUND, more than one fails locally with INVALID_REQUEST naming the ambiguity -- never a silent first match. A draft template (fresh from import/derive, not yet reviewed) is not listed, so address it by the ID import/derive returned until it is approved.
Access model
The existing Root Folder grant is authoritative; template approval is a separate authority on top of it:
- Effective
readon the template's Root Folder permitsshow/instantiate. - Effective
manage(or ownership) permitsderive/update. reviewrequires an activeadmin/global_adminwho is not the template's owner and not the author of its current manifest version -- separation of duties, enforced server-side. Review authority can approve a validated draft; it can never edit template content or grants.
No operation copies grants from a template to the workspace/derived template it produces: the new Root Folder's owner is whoever ran instantiate/derive/import, and sharing it is a separate, explicit tv folder access grant afterward.
list / show
tv template list shows every approved template the caller can currently see with effective read. tv template show <template> adds the manifest's lineage, provenance, version, review evidence (when approved), and a flattened preview of the template's tree (path/kind pairs, folders before their own files, exactly what the ICM tree validator sees).
instantiate / derive
tv template instantiate <template> --name <workspace-name> copies every folder, file, committed version, metadata value, and tag from an approved template into a brand-new Root Folder owned by the caller, classified icm-kind=workspace with lineageFolderId pointing back at the template. tv template derive <template> --name <name> --description <text> does the same but produces a new draft template (icm-kind=template, review-status=draft) the caller now manages, ready for update/review. Both preserve byte-for-byte content and checksums at new storage keys; neither ever produces a half-created Root Folder -- a failure during the copy leaves nothing visible.
import
tv template import <path|zip> --name <name> --description <text> enumerates a local folder or a ZIP archive into a bounded virtual tree, validates it with the same ICM tree validator that governs every existing template, and -- only if that passes -- uploads every file straight to backend-issued canonical staging keys through signed URLs before a new draft template is committed atomically. tv template import --starter --name <name> --description <text> imports the CLI's own bundled two-stage example (packages/cli/assets/icm-starters/, vendored under MIT from a real upstream commit with full provenance in that directory's UPSTREAM.json) through this exact same path -- there is no separate, backend-only way to seed a starter template.
Import safety, enforced entirely client-side before any network request:
- Symlinks (real or ZIP symlink entries) and non-regular files are refused outright.
- Absolute paths, drive/UNC paths,
.., NUL/control characters, and backslash-as-traversal are refused. - Two entries that collide once case-folded are refused (they would collapse to one on a case-insensitive filesystem).
- The tree must fit the hard bounds: at most 128 folders, 128 files, 256 committed versions/objects, hierarchy depth 32, 128 MiB per object, 512 MiB total logical bytes, and (ZIP only) a compressed-to-expanded ratio no worse than 100:1 -- a zip-bomb guard.
- Exactly one root
CLAUDE.mdis required; the whole tree must otherwise satisfy the ICM tree validator (two-digit stage names, oneCONTEXT.mdper stage, anoutputfolder as a stage's only permitted subfolder, no content nested deeper than directly inside a stage or itsoutputfolder).
ZIP archives are enumerated with yauzl in lazy-entry streaming mode -- only central-directory metadata is read during validation, and no entry's content is decompressed until (and unless) that exact file is actually being uploaded. This ZIP-parsing code is CLI-only; it never runs in a Worker. The backend re-validates and re-enforces every one of these properties itself at commit time; the CLI's checks exist purely to fail fast, locally, before wasting a network round trip. A failed prepare, upload, or commit leaves no visible template, and cleanup never touches the source you imported from.
update
tv template update <template> <path|zip> --reason <text> adds a new immutable version to an existing template you manage, exactly like import (same tree validation, same bounds, same signed-upload path) but against a template that already exists. It always increments the manifest's template-version and resets review-status to draft -- a distinct authorized reviewer must approve the new version before the template is usable again.
review
tv template review <template> --approve --reason <text> is how a distinct admin/ global_admin approves a validated draft, binding the approval to the manifest's exact current file version and template-version. There is no --reject -- a draft simply stays a draft, or is superseded by a further update.
Errors
Beyond the identity/authentication/account errors shared by every tv command: NOT_FOUND, TEMPLATE_NOT_FOUND, TEMPLATE_NOT_APPROVED, TEMPLATE_REVIEW_CONFLICT, TEMPLATE_INVALID_MANIFEST, TEMPLATE_WALK_FAILED, TEMPLATE_LINEAGE_INVALID, TEMPLATE_SOURCE_CHANGED, TEMPLATE_IMPORT_INVALID, TEMPLATE_GRAPH_TOO_LARGE, TEMPLATE_COPY_FAILED, and TEMPLATE_VERSION_CONFLICT. Human output and --json both never print a storage URL, an upload API key, a bearer token, or any other raw provenance internal -- only template records, lineage, and tree previews.