Thalix VaultDocumentation
Browse documentation
Architecture
publishedimplementedEdit this page

ICM convention vocabulary

This page is the one canonical place the ICM convention vocabulary is defined (issue #69, BRAIN-07). It rides entirely on the existing file-tag machinery (#58 sets/lists tags, #59 filters tv file find by tag) plus a set of workspace naming conventions applied to folder and file names. Nothing here adds a table, a column, or an RLS policy: the vocabulary is documentation and data, not schema.

packages/contracts/src/icm.ts is the single source of truth for every literal value on this page (ICM_TAG_NAME, ICM_ROLES, ICM_ROLE_FILTER_TOKENS, ICM_ROOT_MAP_FILENAME, ICM_STAGE_CONTRACT_FILENAME, ICM_OUTPUT_FOLDER_NAME, and the isIcmRole/ isIcmStageFolderName predicates). If this page and that module ever disagree, the module wins; fix the page.

File tags vs. folder naming

Two different mechanisms carry ICM meaning, and they are not interchangeable:

  • File tags are real, queryable data (#58/#59): a name=value pair set on one file with tv file tag set, listed with tv file tag list, and filtered with tv file find --tag. The type tag documented below is an ordinary file tag -- no new tag kind, no new storage.
  • Folder naming is a convention, not data: nothing in the Vault today parses a folder's name or enforces its structure server-side. A stage folder's role is signaled entirely by what a human or agent names it and what files it contains, checked only by the client-side predicates this contracts module exports.

Concretely: file tags apply to files only. Root Folders and ordinary folders cannot carry a tag at all today -- tv file tag set/unset/list all take a <file-id>, never a folder ID. A workspace's role (is this Root Folder a template, a live workspace, an archive?) is therefore read from its layout and naming, never from a tag on the folder itself. See Current limitation below.

The type tag and its four roles

Every ICM-classified file carries a type tag whose value is exactly one of four roles:

RoleMeaningFilter token
contractA stage or workspace's contract: what it requires, produces, and how it is judged done.type=contract
stageA working artifact produced or consumed inside a numbered stage folder.type=stage
referenceRead-only supporting material a stage consults but does not modify.type=reference
outputA finished, handed-off artifact -- the result a downstream stage or consumer reads.type=output

ICM_TAG_NAME is "type"; ICM_ROLES is the frozen tuple ["contract", "stage", "reference", "output"] in that order; isIcmRole accepts exactly those four case-sensitive strings and rejects everything else, including case variants -- tag values compare byte-for-byte, case-preserving (#59), never normalized.

Querying by role

tv file find --tag type=<role> (issue #59) is the query surface; no new command, endpoint, or index exists or is needed. Because --tag is repeatable and AND-composes, a caller can combine a role filter with --within to scope to one workspace, or with another --tag to intersect two classifications:

tv file tag set <file-id> type=contract --reason "classify as a stage contract"
tv file find --tag type=contract
tv file find --tag type=stage
tv file find --tag type=reference
tv file find --tag type=output
tv file find --within <root-folder-id> --tag type=output

Each of the four filter tokens above (type=contract, type=stage, type=reference, type=output) is exported byte-for-byte as ICM_ROLE_FILTER_TOKENS.<role> in packages/contracts/src/icm.ts -- callers should read the token from there rather than re-concatenating type= and a role string themselves.

Workspace layout conventions

A workspace (a Root Folder laid out for ICM work) follows a small, fixed set of naming conventions. None of these names is enforced by the server; they are conventions a human or agent applies, and the contracts module's predicates exist so tooling can check them client-side:

  • Root map -- CLAUDE.md (ICM_ROOT_MAP_FILENAME): the single file at the workspace's Root Folder a human or agent reads first. It names the workspace's stages and where each role's files live -- the entry point for a cold read of the workspace.
  • Stage contract -- CONTEXT.md (ICM_STAGE_CONTRACT_FILENAME): every stage folder carries one, describing that stage's inputs, outputs, and completion criteria. A file playing this role is typically also tagged type=contract.
  • Output folder -- output (ICM_OUTPUT_FOLDER_NAME): the folder name a stage's finished, handed-off artifacts are written under. Files under output/ are typically tagged type=output; a downstream stage or consumer reads from here, never from a stage's working area.
  • Stage folder grammar: a stage folder's name is exactly two digits, an underscore, then a lowercase kebab-case slug -- for example 01_intake or 99_publish. isIcmStageFolderName accepts that grammar precisely: any other digit width (1_intake, 001_intake), uppercase (01_Intake), a space or hyphen instead of the underscore, a doubled underscore, or a leading/trailing/doubled hyphen in the slug is rejected.

A typical workspace layout:

<root-folder>/
  CLAUDE.md              # type=contract (root map)
  01_intake/
    CONTEXT.md            # type=contract (stage contract)
    ...working files...    # type=stage
    output/
      ...handoff files...  # type=output
  02_draft/
    CONTEXT.md
    ...
    output/
  99_publish/
    CONTEXT.md
    output/

Handoff convention

A stage's output/ folder is the only place downstream consumers look. A stage in progress may accumulate any number of intermediate type=stage working files, but only files it deliberately moves (or tags) into output/ -- tagged type=output -- are the stage's handoff. This keeps "what did stage N actually deliver" answerable with one query: tv file find --within <stage-folder-id> --tag type=output, without needing to know which of a stage's working files were scratch versus final.

No schema change

Nothing above requires a migration. The type tag is stored, validated, and searched entirely through the existing #58/#59 file-tag columns, functions, and RLS policies; the four role values and the workspace naming conventions live only in packages/contracts/src/icm.ts and this page.

Root Folder classification: reserved root-manifest tags

A Root Folder's own role -- is it an ordinary root, a template someone can instantiate or derive from, or a live instantiated workspace? -- is queryable, but not through a folder-level tag: folders still cannot carry a tag today (tags apply to files only, #58). Instead, issue #68 (BRAIN-06, template system) classifies a Root Folder through three RESERVED tags on its own manifest: the committed, active root-level file named exactly CLAUDE.md (ICM_ROOT_MAP_FILENAME, the same file that already serves as the workspace map above).

TagValuesMeaning
icm-kindtemplate, workspaceWhether this root is a reusable template or an instantiated workspace.
review-statusdraft, approvedWhether an authorized reviewer has approved the current manifest version.
template-versiona semver string (MAJOR.MINOR.PATCH)The manifest's own version; any update increments it and resets review-status to draft.

These three tag names are reserved on a root CLAUDE.md specifically: a generic tv file tag set/tv file tag unset (issue #58) rejects them there exactly like an invalid tag name, indistinguishable from any other malformed name -- only the trusted #68 template RPCs (prepare_template_copy/commit_template_copy, prepare_template_import/ commit_template_import, prepare_template_update/commit_template_update, and review_template) may write them, under a transaction-local capability the database grants for the exact duration of that one call. The pre-existing type tag documented above is never reserved and stays fully generic everywhere, including on a root CLAUDE.md.

Tags alone never prove approval. Approval provenance comes only from a template.reviewed audit event recorded by review_template, bound to the exact manifest file version and template-version value it approved; a later manifest update changes that value and resets review-status to draft, so a stale approval for a superseded version can never be mistaken for current. Review is a separate authority from edit and grant authority: an active administrator may approve a template only if they are neither its owner nor the author of its current manifest version, and approving confers no ability to edit the template's content or its grants.

folders.lineage_folder_id (nullable, root-only, same-organization, immutable after creation) is the one schema addition #68 makes: it records the source template Root Folder a derived template or instantiated workspace came from. Ordinary roots and base templates leave it null. See packages/contracts/src/templates.ts for the exact contract vocabulary and supabase/migrations/20260829190000_issue68_template_system.sql for the RPCs and constraints.