Project templates
Beskid template engine (`beskid.template.v1`), project and item templates, workspace scaffolds, and instantiation semantics.
Document layout: Feature capability layoutconforms
## Purpose## Requirements
Project templates Specification
Purpose
Beskid template engine (beskid.template.v1), project and item templates, workspace scaffolds, and instantiation semantics.
Requirements
Requirement: Beskid-native template engine schema: Decision [D-TOOL-SCAFF-0001]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
The platform must use
beskid.template.v1only. Foreign engine schemas are forbidden in spec, CLI, and pckg.
Stable ID: BSP-REQ-FB3A4BD6BB5D
Legacy source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0001-beskid-template-v1-engine/content.md
Source SHA-256: a9b5835b19b02598646b6129f5b0acd8964a8fb505ea110d61f3a3b79b0c1f29
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Human-readable {{ }} placeholders: Decision [D-TOOL-SCAFF-0002]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Text files must use
{{symbolName}}placeholders; optionalsourceNamerewriting applies to paths and identifiers.
Stable ID: BSP-REQ-BFCF440720C4
Legacy source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0002-placeholder-delimiters/content.md
Source SHA-256: 3ed3651cc936a19e90960b3f3a80c38958f8f2ee2812adc0d2987c4692ae5679
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Templates need not build at template root: Decision [D-TOOL-SCAFF-0003]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Template packages need not compile at the template project root. Tooling must validate via instantiation output builds.
Stable ID: BSP-REQ-BD6B78D79821
Legacy source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0003-no-runnable-template-root/content.md
Source SHA-256: e015a9fa54650dc994d0dde94bd28b9b2fbd0db593ce5fcfc7efc292b2cf0130
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Implicit corelib: Decision [D-TOOL-SCAFF-0004]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Inject corelib; forbid noCorelib flags.
Stable ID: BSP-REQ-FC2577A63315
Legacy source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0004-corelib-always-on/content.md
Source SHA-256: cb39fe4bcaaf78240c2a6940bb153e4b4dbffe142e499269d613feeb55ce18d9
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Three template kinds: Decision [D-TOOL-SCAFF-0005]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Support all three via tags.type.
Stable ID: BSP-REQ-34EFB272A162
Legacy source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0005-template-kinds-v1/content.md
Source SHA-256: 5d7cdb365da8b26065721bf63ded7fb5f57fa44a3487c4f035b4c26b51bbdd5a
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Path and git sources: Decision [D-TOOL-SCAFF-0006]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Resolve registry, path, and git.
Stable ID: BSP-REQ-FB3B998BE6B5
Legacy source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0006-path-and-git-sources/content.md
Source SHA-256: f862a47ae372fd6057017d9d3c56c63fe2542e7da5ce53a46e6553e26e5153bd
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: No constraints: Decision [D-TOOL-SCAFF-0007]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
No constraint blocks in schema.
Stable ID: BSP-REQ-C5A07BB7E838
Legacy source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0007-no-constraint-blocks/content.md
Source SHA-256: 557f2a3f6255e1b9536fb781bf2277d45437cee008e8c40d87fd2863f54136b5
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Post-actions: Decision [D-TOOL-SCAFF-0008]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Documented action ids, no v1 whitelist.
Stable ID: BSP-REQ-12AD223BBC13
Legacy source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0008-unrestricted-post-actions/content.md
Source SHA-256: 1ad088d737a8c4f00c6124868c0eb756ae980f4c9aa5d63b6710b175e7e610f4
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Update on use: Decision [D-TOOL-SCAFF-0009]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Compare cache on every use when online.
Stable ID: BSP-REQ-D1B27D8A4357
Legacy source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0009-update-check-on-use/content.md
Source SHA-256: 09bd4a0943e1d45e32489ed7993b869747f990dec4690e67a49b6d4c04e9e7c3
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Yanked warning: Decision [D-TOOL-SCAFF-0010]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Warn on yanked; optional continue flag.
Stable ID: BSP-REQ-2D58D52056EA
Legacy source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0010-yanked-template-warning/content.md
Source SHA-256: bb92e83cf111f651ac9039e6e045e4a868310ad9319bb89640b3125c8473fcc5
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Informative Source Provenance
The records below preserve migration history and are not normative except where text was extracted into a requirement above.
Source Record: Project templates
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/content.md
SHA-256: 528b7d6a435a280ae90ab6850e72e3afbd4ee749f1720469fb3cce2bb52763c9
Migrated source text
<SpecSection title="What this feature specifies" id="what-this-feature-specifies">
**Project templates** — `beskid.template.v1`, project/workspace/item scaffolding, `{{ }}` placeholders, GUID regeneration, path/git/registry sources, update checks, and corelib policy on instantiated hosts.
</SpecSection>
<SpecSection title="Implementation anchors" id="implementation-anchors">
- `compiler/crates/beskid_template` — `beskid.template.v1` engine and `service` orchestration
- `compiler/crates/beskid_cli/src/commands/new.rs` — Clap wrapper for `beskid new`
- `compiler/crates/beskid_analysis/src/projects/model.rs` — `ProjectKind::Template`
- `compiler/crates/beskid_pckg_server/Services/PackageArtifactValidator.cs` — template profile
- First-party packages `beskid.templates.console`, `beskid.templates.lib`, `beskid.templates.project`
</SpecSection>
<SpecSection title="Contract statement" id="contract-statement">
Beskid **templates** are versioned trees of files plus a **`beskid.template.v1`** manifest that drives **project**, **workspace**, and **item** scaffolding. Templates **may** be sourced from a **pckg template package**, a **local directory**, or a **git URL** (with optional subdirectory and ref). Instantiation **must** support **interactive prompts** and **non-interactive CLI flags** for the same symbol set.
Template content is **not** required to build as a standalone runnable project before instantiation; authors validate templates by instantiating into a scratch directory and running `beskid build` / `beskid test` on the **output**.
Any host project shape (**`Host`**, **`Mod`**, or future kinds) **may** appear in template output—templates impose no restriction on scaffolded `project.type` or targets.
</SpecSection>
<SpecSection title="Inputs and outputs" id="inputs-and-outputs">
| Input | Description |
| --- | --- |
| Template source | Registry package (`packageKind: template`), filesystem path, or git remote + ref |
| User parameters | Symbol values from flags and/or prompts |
| Output location | New directory, existing project root (item templates), or workspace root |
| **Output** | Files on disk: optional **`Workspace.proj`**, one or more **`Project.proj`** trees, `.bd` sources, supporting assets |
| Side effects | **Post-actions** (see [flow](./flow-and-algorithm/)); **`beskid lock`** recommended for project/workspace outputs |
| **Corelib** | Every instantiated **host** project **must** resolve **corelib** implicitly—see [design model](./design-model/#corelib-policy) |
</SpecSection>
<SpecSection title="State model" id="state-model">
| State | Location | Lifecycle |
| --- | --- | --- |
| **Installed template snapshot** | User-level tooling cache (exact path normative in [beskid new](./../beskid-new/design-model/)) | Updated on `beskid new install` and when **update check** finds a newer registry version |
| **Template manifest** | `.beskid/template.json` at template root inside package or git checkout | Immutable for a given package version / git ref |
| **Authoring project** | `Project.proj` with **`type: Template`** | Published as template package; excluded from normal app compile graphs |
| **Instantiation session** | Ephemeral | Symbol table → file operations → post-actions |
</SpecSection>
<SpecSection title="Algorithms and flow" id="algorithms-and-flow">
High-level flow is specified in **[flow and algorithm](./flow-and-algorithm/)**. Update detection runs **on each use** of a template (registry, path, or git) before instantiation.
</SpecSection>
<SpecSection title="Edge cases and errors" id="edge-cases-and-errors">
- **Yanked** registry template version: CLI **must** emit a **warning** and **may** continue if the user does not abort (see [contracts](./contracts-and-edge-cases/)).
- **Output exists**: project/workspace templates **must** fail by default when the target directory is non-empty unless `--force` (exact flag in [beskid new](../beskid-new/)).
- **Item template** target not inside a discovered `Project.proj`: **must** error with E19xx diagnostic.
- **Invalid `{{ }}`**: unresolved placeholders after substitution **must** fail instantiation.
- **GUID list**: every guid in the `guids` array **must** be replaced with a newly generated guid preserving source format—see [design model](./design-model/#guids).
</SpecSection>
<SpecSection title="Compatibility and versioning" id="compatibility-and-versioning">
- Engine schema: **`beskid.template.v1`** (breaking changes require new schema id and migration notes).
- Template packages use the same **registry-assigned semver** as library packages; template identity string includes package id and version.
- **No host constraints** block (OS, SDK version, etc.)—templates are always eligible when installed.
</SpecSection>
<SpecSection title="Security and performance notes" id="security-and-performance-notes">
- **Post-actions** are **not** whitelist-gated; manifest declares action ids and arguments. Hosts **must** document supported actions; unknown actions **should** warn and skip (not fail entire run) unless `--strict-post-actions` is set (see [beskid new](../beskid-new/)).
- Git templates **must** pin ref by default (`--git-ref`); floating default branch is allowed only when explicitly requested.
- Large template trees **should** use `sources.exclude` aggressively to avoid copying build artifacts.
</SpecSection>
<SpecSection title="Examples" id="examples">
See **[examples](./examples/)** for console app, class library, workspace, item, and template-authoring samples.
</SpecSection>
<SpecSection title="Verification and traceability" id="verification-and-traceability">
- Schema fixtures: `beskid.template.v1` golden files under planned `compiler/crates/beskid_tests/src/projects/templates/`
- Round-trip: instantiate → `beskid lock` → `beskid build` for first-party `beskid.templates.*`
- Item template: add file under existing `Project.proj` → `beskid analyze` clean
</SpecSection>
<SpecSection title="Decisions" id="decisions">
No open decisions. Closed choices are normative ADRs under **`adr/`** (`D-TOOL-SCAFF-0001` … `D-TOOL-SCAFF-0010`); use the reader **ADRs** tab for detail. Legacy [decisions record](./decisions-record/) is a migration index only.
</SpecSection>
<SpecSection title="Related features" id="related-features">
- **[Template packages](../template-packages/)** — `packageKind`, `.bpk` layout, pckg UI
- **[beskid new](../beskid-new/)** — commands and cache
- **[Package kinds](/platform-spec/tooling/registry-client/package-kinds/)** — `template` vs `library` vs reserved `tool`
</SpecSection>
## Decisions
<!-- spec:generate:adr-index -->
No open decisions. Closed choices are normative ADRs under **`adr/`** (`D-TOOL-SCAFF-0001` … `D-TOOL-SCAFF-0010`); use the reader **ADRs** tab for expandable detail.
<!-- /spec:generate:adr-index -->
## Articles
<!-- spec:generate:article-index -->
- [Contracts and edge cases](./articles/contracts-and-edge-cases/)
- [Decisions record (legacy index)](./articles/decisions-record/)
- [Design model](./articles/design-model/)
- [Examples](./articles/examples/)
- [FAQ and troubleshooting](./articles/faq-and-troubleshooting/)
- [Flow and algorithm](./articles/flow-and-algorithm/)
- [Verification and traceability](./articles/verification-and-traceability/)
<!-- /spec:generate:article-index -->
Source Record: Beskid-native template engine schema
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/adr/0001-beskid-template-v1-engine/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0001-beskid-template-v1-engine/content.md
SHA-256: a9b5835b19b02598646b6129f5b0acd8964a8fb505ea110d61f3a3b79b0c1f29
Migrated source text
## Context
Foreign template engines would split validation and documentation across ecosystems.
## Decision
The platform **must** use **`beskid.template.v1`** only. Foreign engine schemas are forbidden in spec, CLI, and pckg.
## Consequences
Single parser and validator in tooling; template docs stay in-repo.
## Verification anchors
CI grep excluding foreign schema identifiers under `compiler/` and platform-spec tooling tree.
Source Record: Human-readable {{ }} placeholders
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/adr/0002-placeholder-delimiters/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0002-placeholder-delimiters/content.md
SHA-256: 3ed3651cc936a19e90960b3f3a80c38958f8f2ee2812adc0d2987c4692ae5679
Migrated source text
## Context
Template authors need delimiter syntax distinct from Beskid source.
## Decision
Text files **must** use **`{{symbolName}}`** placeholders; optional **`sourceName`** rewriting applies to paths and identifiers.
## Consequences
Editors can highlight unmatched braces; substitution tests stay deterministic.
## Verification anchors
Golden substitution tests under planned `beskid_tests` template fixtures.
Source Record: Templates need not build at template root
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/adr/0003-no-runnable-template-root/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0003-no-runnable-template-root/content.md
SHA-256: e015a9fa54650dc994d0dde94bd28b9b2fbd0db593ce5fcfc7efc292b2cf0130
Migrated source text
## Context
Requiring `beskid build` on template sources slows authoring and blocks non-host template layouts.
## Decision
Template packages **need not** compile at the template project root. Tooling **must** validate via instantiation output builds.
## Consequences
Faster template iteration; CI runs instantiate-then-build on consumer output.
## Verification anchors
`beskid.templates.*` CI pipeline; planned `beskid_tests` template fixtures.
Source Record: Implicit corelib
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/adr/0004-corelib-always-on/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0004-corelib-always-on/content.md
SHA-256: cb39fe4bcaaf78240c2a6940bb153e4b4dbffe142e499269d613feeb55ce18d9
Migrated source text
## Context
Hosts need standard library.
## Decision
Inject corelib; forbid noCorelib flags.
## Consequences
Simpler manifests.
## Verification anchors
E18xx linter.
Source Record: Three template kinds
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/adr/0005-template-kinds-v1/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0005-template-kinds-v1/content.md
SHA-256: 5d7cdb365da8b26065721bf63ded7fb5f57fa44a3487c4f035b4c26b51bbdd5a
Migrated source text
## Context
Workspace, project, item flows.
## Decision
Support all three via tags.type.
## Consequences
One engine.
## Verification anchors
Three beskid_tests fixtures.
Source Record: Path and git sources
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/adr/0006-path-and-git-sources/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0006-path-and-git-sources/content.md
SHA-256: f862a47ae372fd6057017d9d3c56c63fe2542e7da5ce53a46e6553e26e5153bd
Migrated source text
## Context
Local workflows.
## Decision
Resolve registry, path, and git.
## Consequences
beskid new flags.
## Verification anchors
Git/path integration tests.
Source Record: No constraints
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/adr/0007-no-constraint-blocks/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0007-no-constraint-blocks/content.md
SHA-256: 557f2a3f6255e1b9536fb781bf2277d45437cee008e8c40d87fd2863f54136b5
Migrated source text
## Context
DSL delay.
## Decision
No constraint blocks in schema.
## Consequences
Simpler v1.
## Verification anchors
Reject constraints key.
Source Record: Post-actions
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/adr/0008-unrestricted-post-actions/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0008-unrestricted-post-actions/content.md
SHA-256: 1ad088d737a8c4f00c6124868c0eb756ae980f4c9aa5d63b6710b175e7e610f4
Migrated source text
## Context
Extensibility.
## Decision
Documented action ids, no v1 whitelist.
## Consequences
Operator sandbox policy.
## Verification anchors
Action registry docs.
Source Record: Update on use
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/adr/0009-update-check-on-use/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0009-update-check-on-use/content.md
SHA-256: 09bd4a0943e1d45e32489ed7993b869747f990dec4690e67a49b6d4c04e9e7c3
Migrated source text
## Context
Stale cache.
## Decision
Compare cache on every use when online.
## Consequences
Update prompts.
## Verification anchors
Mock HTTP test.
Source Record: Yanked warning
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/adr/0010-yanked-template-warning/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/adr/0010-yanked-template-warning/content.md
SHA-256: bb92e83cf111f651ac9039e6e045e4a868310ad9319bb89640b3125c8473fcc5
Migrated source text
## Context
Yank policy.
## Decision
Warn on yanked; optional continue flag.
## Consequences
Registry alignment.
## Verification anchors
Yank API test.
Source Record: Contracts and edge cases
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/articles/contracts-and-edge-cases/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/articles/contracts-and-edge-cases/content.md
SHA-256: fd20102eb686b7651e09cb54855d7d6b3e8aac09459c6ae09012491c81a0d0b9
Migrated source text
## Purpose and scope
Testable rules for template resolution, instantiation, registry interaction, and diagnostics (**E1901–E1999**).
## Resolution
| ID | Rule |
| --- | --- |
| T-R01 | `beskid new` **must** accept exactly one template selector: `shortName`, `--package <id>[@version]`, `--path <dir>`, or `--git <url>` (with optional `--git-ref`, `--git-subpath`). |
| T-R02 | Registry resolution **must** require `packageKind: template` on the resolved `package.json`. |
| T-R03 | First-party ids under **`beskid.templates.*`** **must** be preferred when `shortName` is ambiguous and registry is configured. |
| T-R04 | On **every** template use, tooling **must** compare cached install (if any) to latest non-yanked registry version and **must** print an informational message when a newer version exists. |
| T-R05 | When the resolved version is **yanked**, tooling **must** print a **warning** naming the package and version; exit code **0** if the user proceeds via `--allow-yanked` or interactive confirmation. |
## Instantiation
| ID | Rule |
| --- | --- |
| T-I01 | **Project** templates **must** create the output directory when missing; **must** error when non-empty without `--force`. |
| T-I02 | **Workspace** templates **must** emit `Workspace.proj` at the workspace root and member `Project.proj` files at declared member paths. |
| T-I03 | **Item** templates **must** require `-o` / `--output` pointing at a file or directory under a folder containing `Project.proj` (or pass `--project` to disambiguate). |
| T-I04 | After substitution, **no** `{{` `}}` placeholder tokens **may** remain in output files. |
| T-I05 | All `guids` entries **must** be replaced in output; leftover source guids **must** fail with **E1906**. |
| T-I06 | Instantiated host projects **must** receive corelib per [design model](./design-model/#corelib-policy); templates **must not** emit opt-out flags. |
| T-I07 | Templates **may** scaffold **`Mod`**, multi-target, or FFI-heavy projects without restriction. |
## Interactive and flags
| ID | Rule |
| --- | --- |
| T-U01 | When stdin is a TTY and `preferInteractive` is true or any required symbol lacks a value, the CLI **must** prompt. |
| T-U02 | When `--no-interactive` is set, only flags and defaults **may** be used; missing required symbols **must** fail with **E1903**. |
| T-U03 | `--symbol` (repeatable) and `-n` / `--name` for the primary name symbol **must** be supported. |
## Post-actions
| ID | Rule |
| --- | --- |
| T-P01 | `postActions` is an ordered array of `{ "actionId": string, "args": object }`. |
| T-P02 | There is **no** platform whitelist; hosts **must** document supported `actionId` values. |
| T-P03 | Unknown `actionId` **should** log a warning and continue unless `--strict-post-actions`. |
| T-P04 | Built-in actions **must** include at minimum: `runCommand`, `beskidLock`, `beskidFetch`, `openReadme`. |
## Builtin forms
| Form id | Input | Output |
| --- | --- | --- |
| `identity` | string | unchanged |
| `lowerCase` | string | lowercase |
| `upperCase` | string | uppercase |
| `safeName` | string | filesystem-safe identifier |
| `namespace` | string | dotted namespace from path-like name |
## Edge cases
- **Git shallow clone failure** → **E1907** with remediation (network, auth, ref).
- **Template package contains `packageKind: library`** → **E1902** reject for `beskid new install`.
- **Item template overwrites existing file** → require `--force` or interactive confirm.
- **Workspace template with duplicate member ids** → **E1908** at validation time (before write).
## Diagnostic band E1901–E1999
| Code | Meaning |
| --- | --- |
| E1901 | Template manifest missing or invalid schema |
| E1902 | Package kind is not `template` |
| E1903 | Required symbol not provided |
| E1904 | Output path conflict |
| E1905 | Item template outside project root |
| E1906 | GUID replacement incomplete |
| E1907 | Git template source failed |
| E1908 | Workspace template invalid member graph |
| E1999 | Reserved internal template engine error |
Source Record: Decisions record (legacy index)
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/articles/decisions-record/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/articles/decisions-record/content.md
SHA-256: ac999cee7f05c73739d9cf00e37b23eef2ce6ee3f893da86e3e7c1713a0ba784
Migrated source text
## Purpose
Legacy URL retained for bookmarks. Normative decision text **must** be read from **`adr/`** (`D-TOOL-SCAFF-0001` … `D-TOOL-SCAFF-0010`) and the feature hub **ADRs** tab.
## Canonical ADR index
| adrId | Title |
| --- | --- |
| D-TOOL-SCAFF-0001 | [Beskid-native template engine](./adr/0001-beskid-template-v1-engine/) |
| D-TOOL-SCAFF-0002 | [{{ }} placeholders](./adr/0002-placeholder-delimiters/) |
| D-TOOL-SCAFF-0003 | [No runnable template root](./adr/0003-no-runnable-template-root/) |
| D-TOOL-SCAFF-0004 | [corelib always on](./adr/0004-corelib-always-on/) |
| D-TOOL-SCAFF-0005 | [Template kinds v1](./adr/0005-template-kinds-v1/) |
| D-TOOL-SCAFF-0006 | [Path and git sources](./adr/0006-path-and-git-sources/) |
| D-TOOL-SCAFF-0007 | [No constraint blocks](./adr/0007-no-constraint-blocks/) |
| D-TOOL-SCAFF-0008 | [Post-actions catalog](./adr/0008-unrestricted-post-actions/) |
| D-TOOL-SCAFF-0009 | [Update check on use](./adr/0009-update-check-on-use/) |
| D-TOOL-SCAFF-0010 | [Yanked template warning](./adr/0010-yanked-template-warning/) |
Source Record: Design model
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/articles/design-model/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/articles/design-model/content.md
SHA-256: 9ccc5455b25dab56a9f67880fef0a701253674f22c986bfdb72ffced2b1ced33
Migrated source text
## Purpose and scope
This article defines the **normative data model** for Beskid templates: manifest file location, `beskid.template.v1` top-level keys, symbol types, content manipulation, and **corelib** behavior on instantiated projects.
## Manifest location
| Context | Path |
| --- | --- |
| Template package or git/path tree | **`.beskid/template.json`** at the template root |
| Optional author metadata in `Project.proj` | `project.template { shortName, identity }` when **`type: Template`** |
The engine **must** read **`.beskid/template.json`** as authoritative. `project.template` keys are hints for discovery and packaging; if both exist, **`template.json` wins** on conflict.
## Top-level `beskid.template.v1` keys
| Key | Required | Meaning |
| --- | --- | --- |
| `schema` | yes | Literal **`beskid.template.v1`** |
| `identity` | yes | Stable id: `packageId::version` or path/git fingerprint |
| `name` | yes | Display name in `beskid new list` |
| `shortName` | yes | CLI selector: `beskid new <shortName>` |
| `author` | no | Author string |
| `description` | no | Short summary |
| `classifications` | no | Search tags |
| `tags` | no | Object; **`type`** **must** be one of: `project`, `workspace`, `item` |
| `sourceName` | no | Default token replaced in paths and file bodies (in addition to `{{ }}`) |
| `symbols` | no | Parameter definitions |
| `sources` | no | File copy rules; default single mapping `./` → output |
| `guids` | no | GUIDs to regenerate in output |
| `forms` | no | Value transforms (see below) |
| `postActions` | no | Ordered post-instantiation actions |
| `preferInteractive` | no | When true, prompt for symbols without CLI values before using defaults |
**Forbidden in v1:** `constraints`, foreign schema imports, or alternate placeholder delimiters.
## Tags.type semantics
| `tags.type` | Output |
| --- | --- |
| `project` | Creates a directory tree with **`Project.proj`** (and `Src/` or template-defined layout) |
| `workspace` | Creates **`Workspace.proj`** plus member project trees |
| `item` | Adds or overwrites files **inside** an existing project directory; **must not** emit a new root `Project.proj` unless `sources` explicitly includes one and the CLI passed `--allow-project-manifest` |
## Symbols
Each symbol is an object keyed by symbol id (used in `{{symbolId}}`):
| Field | Required | Meaning |
| --- | --- | --- |
| `type` | yes | `string`, `choice`, `bool`, `integer` |
| `description` | no | Prompt text |
| `defaultValue` | no | Used when non-interactive and flag omitted |
| `choices` | for `choice` | Allowed values |
| `isRequired` | no | Default false; when true, interactive mode **must** collect a value |
CLI mapping: `--symbol <id>=<value>` and short forms documented in [beskid new](../beskid-new/contracts-and-edge-cases/).
## Placeholders
1. **`{{symbolId}}`** — replaced in all processed text files after `forms` are applied.
2. **`sourceName`** — when set at template level, every occurrence of the `sourceName` string in **paths and file contents** is replaced with the primary name symbol (default symbol id **`name`** unless `nameSymbol` specifies another).
Hosts **must** fail if any `{{...}}` remains after substitution.
## Forms
`forms` define named transforms applied to symbol values before substitution (for example `lowerCase`, `safeName`, `namespace`). Built-in form ids are listed in [contracts and edge cases](./contracts-and-edge-cases/#builtin-forms).
## Sources
Each source entry:
| Field | Default | Meaning |
| --- | --- | --- |
| `source` | `./` | Path inside template root |
| `target` | `./` | Relative to user output directory |
| `include` | `["**/*"]` | Glob includes |
| `exclude` | build artifacts, `.beskid/template.json` copy to wrong place | Glob excludes |
| `copyOnly` | `[]` | Copy without text processing |
| `rename` | `{}` | Explicit renames after symbol resolution |
| `condition` | true | When false, skip this source block |
| `modifiers` | `[]` | Conditional patches to include/exclude |
**`.beskid/template.json`** **must not** appear in generated output unless a source block intentionally copies it to a documentation path.
## Guids
The `guids` array lists GUID strings appearing in template sources. For each entry, the engine **must** generate a new GUID and replace **all** occurrences in output, preserving **format and casing** of each occurrence (same rules as common template GUID rewriting: match `N`, `D`, `B`, `P`, `X` representations independently).
## corelib policy
| Rule | Normative statement |
| --- | --- |
| Implicit dependency | Every **instantiated** project with `project.type` absent or **`Host`** **must** treat **corelib** as an **implicit registry dependency** resolved by the toolchain—equivalent to today’s std/corelib discovery path. |
| Manifest surface | **`Project.proj` emitted by templates must not** declare a `noCorelib`, `useCorelib: false`, or any flag that disables corelib. |
| Template author docs | Templates **may** omit an explicit `dependency "corelib"` block from generated manifests; the **first** `beskid lock` / `fetch` **must** materialize corelib anyway. |
| Mod / workspace | **Mod** projects in template output follow mod resolution rules; corelib policy applies to **host** members that execute Beskid user code. |
If the compiler today requires an explicit dependency for some workflows, tooling **must** inject the dependency during instantiation or in post-action **`addCorelib`**—without exposing a user-facing opt-out.
## Authoring: `project.type = Template`
```text
project {
name = "beskid-templates-console"
version = "0.0.0"
type = Template
template {
shortName = "console"
identity = "beskid.templates.console"
}
}
```
`Template` projects **must not** be selected as compile targets for `beskid build` at the template package root; only their packaged `.beskid/template.json` is consumed by consumers.
## Diagram
```arch
flowchart TB
TJ[.beskid/template.json]
SYM[symbols]
SRC[sources]
PH["{{ }} + sourceName"]
GD[guids]
OUT[Output tree]
TJ --> SYM --> PH
TJ --> SRC --> PH
TJ --> GD --> OUT
PH --> OUT
```
## Code anchors
- Planned: `compiler/crates/beskid_cli/src/template/` (engine)
- Manifest parse: extend `compiler/crates/beskid_analysis/src/projects/model.rs` with `ProjectKind::Template`
- Registry: `pckg` template package profile
Source Record: Examples
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/articles/examples/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/articles/examples/content.md
SHA-256: 54cc75666f0e70f0be842abe0aca3a0277dfc1b4a50f674cec7b42a9aaac4904
Migrated source text
## Purpose and scope
Illustrative templates for the three first-party packages and common user flows.
## Console application (`beskid.templates.console`)
**`tags.type`:** `project`
```json
{
"schema": "beskid.template.v1",
"identity": "beskid.templates.console::1.0.0",
"name": "Beskid Console Application",
"shortName": "console",
"description": "Executable app with Main.bd and App target",
"tags": { "type": "project" },
"sourceName": "MyApp",
"symbols": {
"name": {
"type": "string",
"description": "Project name",
"defaultValue": "MyApp",
"isRequired": true
}
},
"sources": [{ "source": "./content/", "target": "./" }],
"postActions": [
{ "actionId": "beskidLock", "args": {} },
{ "actionId": "openReadme", "args": { "path": "README.md" } }
]
}
```
Generated `Project.proj` (excerpt—note **no** corelib opt-out; no explicit corelib dependency required in template):
```text
project {
name = "{{name}}"
version = "0.1.0"
root = "Src"
}
target "app" {
kind = App
entry = "Main.bd"
}
```
**CLI:** `beskid new console -n MyGame -o ./MyGame`
## Class library (`beskid.templates.lib`)
**`tags.type`:** `project`
```json
{
"schema": "beskid.template.v1",
"identity": "beskid.templates.lib::1.0.0",
"name": "Beskid Class Library",
"shortName": "lib",
"tags": { "type": "project" },
"sourceName": "MyLib",
"symbols": {
"name": { "type": "string", "isRequired": true, "defaultValue": "MyLib" }
},
"sources": [{ "source": "./content/", "target": "./" }]
}
```
```text
target "lib" {
kind = Lib
entry = "Lib.bd"
}
```
## Template authoring (`beskid.templates.project`)
**`tags.type`:** `project` (scaffolds a `type: Template` authoring tree)
```json
{
"schema": "beskid.template.v1",
"identity": "beskid.templates.project::1.0.0",
"name": "Beskid Template Package",
"shortName": "template",
"tags": { "type": "project" },
"symbols": {
"name": { "type": "string", "isRequired": true },
"shortName": { "type": "string", "isRequired": true }
},
"sources": [{ "source": "./content/", "target": "./" }]
}
```
Emitted author `Project.proj`:
```text
project {
name = "{{name}}"
version = "0.1.0"
type = Template
template {
shortName = "{{shortName}}"
identity = "{{name}}"
}
}
```
Includes stub **`.beskid/template.json`** with placeholders for the author to complete.
## Workspace template (illustrative)
```json
{
"schema": "beskid.template.v1",
"identity": "beskid.templates.workspace-demo::1.0.0",
"name": "Two-member workspace",
"shortName": "workspace-demo",
"tags": { "type": "workspace" },
"symbols": {
"workspaceName": { "type": "string", "defaultValue": "MyWorkspace" }
},
"sources": [
{ "source": "./workspace/", "target": "./" }
]
}
```
## Item template — contract file
```json
{
"schema": "beskid.template.v1",
"identity": "beskid.templates.contract-item::1.0.0",
"name": "Contract stub",
"shortName": "contract",
"tags": { "type": "item" },
"symbols": {
"contractName": { "type": "string", "isRequired": true }
},
"sources": [
{
"source": "./item/Contract.bd",
"target": "./Src/{{contractName}}.bd"
}
]
}
```
**CLI:** `beskid new contract --symbol contractName=Payment -o ./Src/Payment.bd --project ./MyApp`
## Git and path usage
```bash
beskid new --path ./local-templates/console -n Demo -o ./Demo
beskid new --git https://example.com/templates.git --git-ref v1.2.0 --git-subpath console -n Demo -o ./Demo
beskid new install beskid.templates.console
```
Source Record: FAQ and troubleshooting
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/articles/faq-and-troubleshooting/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/articles/faq-and-troubleshooting/content.md
SHA-256: e03ae9c6263a7e6f02c6ae041d46db4a4db698412e569e4d54d3991b15888644
Migrated source text
## Purpose and scope
Operator and author FAQs.
## Why is there no `useCorelib: false`?
Host projects always receive **corelib** through toolchain resolution. Templates must not document or generate an opt-out; see [design model](./design-model/#corelib-policy).
## How do first-party templates ship?
Only as **`beskid.templates.*`** packages on **pckg**. The CLI downloads them when the registry is available; it does not embed stale copies when updates exist.
## Can I scaffold a compiler mod?
Yes. Template output may set `type: Mod` and include `project.mod { ... }` per [Project manifest contract](/platform-spec/tooling/manifests-and-lockfiles/project-manifest-contract/).
## Item vs project template?
| Kind | Command shape |
| --- | --- |
| Project | `beskid new console -o ./MyApp` |
| Item | `beskid new contract -o ./Src/File.bd --project ./MyApp` |
| Workspace | `beskid new workspace-demo -o ./MyWs` |
## Template build fails at template root
Expected: **`type: Template`** projects are not app compile targets. Run `beskid new` into a scratch folder to validate.
## Update message on every run
By design. Install with `beskid new install <package>` to refresh the cache.
## Yanked template still works
You received a **warning**. Prefer installing a non-yanked version; use `--allow-yanked` only when intentional.
## Placeholder left in file
**E1904** — a `{{symbol}}` was not bound. Pass `--symbol` or run interactive mode.
## pckg page shows no API docs for my template
Correct for `packageKind: template`. See [template packages](../template-packages/).
Source Record: Flow and algorithm
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/articles/flow-and-algorithm/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/articles/flow-and-algorithm/content.md
SHA-256: 9f66e0813b80de86d87659581c8da88e38a3a790d4615feb48fb2453ef9a5c22
Migrated source text
## Purpose and scope
Normative ordering for `beskid new install`, `beskid new list`, and `beskid new <shortName>` (and equivalents).
## Install (`beskid new install`)
1. Resolve source (registry id, `--path`, or `--git`).
2. For registry: download `.bpk` or extract template roots per [template packages](../template-packages/); verify `packageKind: template`.
3. Locate `.beskid/template.json`; parse and validate `beskid.template.v1`.
4. Store snapshot in tooling cache keyed by `identity`.
5. Register `shortName` → cache entry for `beskid new list`.
## List (`beskid new list`)
1. Merge **installed cache** entries with optional **registry search** (when `--online`).
2. Display: `shortName`, `name`, `tags.type`, package id + version, yanked marker if applicable.
## Instantiate (`beskid new`)
```arch
sequenceDiagram
participant User
participant CLI as beskid new
participant Cache
participant Registry as pckg
participant Engine as template engine
participant FS as output filesystem
User->>CLI: select template + output path
CLI->>Cache: load snapshot
alt registry template
CLI->>Registry: query latest version
Registry-->>CLI: version metadata
CLI-->>User: warn if yanked or update available
end
CLI->>Engine: load template.json
Engine->>User: interactive prompts optional
CLI->>Engine: symbol values
Engine->>FS: apply sources, placeholders, guids
Engine->>CLI: postActions list
CLI->>CLI: run postActions
CLI->>CLI: ensure corelib via lock/fetch policy
```
### Step detail
1. **Resolve template** — cache hit by `shortName`, or one-shot path/git without install.
2. **Update check** — for registry-backed templates, query latest semver; if greater than cached, print: `A newer template version is available: <id>@<ver>. Run 'beskid new install <id>' to update.`
3. **Yanked check** — if requested version is yanked, emit warning (see contracts).
4. **Load manifest** — `beskid.template.v1`.
5. **Collect symbols** — merge CLI flags, defaults, interactive prompts.
6. **Evaluate conditions** — skip source blocks whose `condition` is false.
7. **Process sources** — copy, text-process, rename; apply `copyOnly` without substitution.
8. **Substitute** — `{{ }}`, `sourceName`, `forms`.
9. **Regenerate guids** — per `guids` array.
10. **Post-actions** — sequential; failures honor `--strict-post-actions`.
11. **Corelib** — run default post-action `beskidLock` (or equivalent) so corelib is materialized without user declaring opt-out.
## Workspace templates
1. Write `Workspace.proj` first (substituted).
2. For each member in `workspaceMembers` symbol or fixed layout in `sources`, write member `Project.proj` and sources.
3. Post-action `beskidLock` at workspace root **must** lock all members.
`workspaceMembers` is a `choice` or structured symbol when the template offers optional members; fixed layouts **may** omit the symbol.
## Item templates
1. Resolve host `Project.proj` root.
2. Apply `sources` with `target` relative to chosen file path.
3. **Must not** run workspace-level lock unless `--lock` passed.
## Git and path sources
| Source | Behavior |
| --- | --- |
| `--path` | Read `.beskid/template.json` directly; no cache unless `--install` |
| `--git` | Clone to cache subdirectory keyed by url+ref; optional sparse checkout for `subpath` |
## Implementation anchors
- `compiler/crates/beskid_cli/src/commands/new.rs` (planned)
- `compiler/crates/beskid_pckg` registry fetch (existing client)
Source Record: Verification and traceability
Authority: informative provenance
Legacy path: /platform-spec/tooling/project-scaffolding/project-templates/articles/verification-and-traceability/
Source: site/spec-content/platform-spec/tooling/project-scaffolding/project-templates/articles/verification-and-traceability/content.md
SHA-256: 15eabdd4ebd1722ca2932031e16b70577549a62614f418b0704b0cf5ea1e672d
Migrated source text
## Purpose and scope
How implementers prove template engine and first-party package compliance.
## Conformance matrix
| Requirement | Verification |
| --- | --- |
| `beskid.template.v1` parse | JSON schema fixture tests in `beskid_tests` |
| `{{ }}` exhaustion | Golden output diff tests |
| GUID rewrite | Multi-format guid fixture files in template content |
| corelib after instantiate | `beskid lock` + compile smoke without `noCorelib` manifest key |
| Update check on use | Mock registry returning newer semver → expect stdout message |
| Yanked warning | Publish yanked version → `beskid new` warns |
| Item template | Instantiate into temp project → `beskid analyze` |
| Workspace template | Two members resolve in `beskid tree` |
## First-party packages
| Package id | shortName | tags.type |
| --- | --- | --- |
| `beskid.templates.console` | `console` | `project` |
| `beskid.templates.lib` | `lib` | `project` |
| `beskid.templates.project` | `template` | `project` |
CI **must** pack and publish these under the **`beskid.templates.*`** namespace when registry credentials are available, matching [corelib publish](/platform-spec/core-library/compiler-integration/corelib-discovery-and-packaging/) workflow patterns.
## pckg server
- Template `.bpk` **must** reject `packageKind: library` when `template.json` present (or require `template`).
- Template package page **must not** mount API documentation viewer (see [template packages](../template-packages/verification-and-traceability/)).
## Spec drift
Changes to `beskid.template.v1` **must** update this article and [design model](./design-model/) in the same change set.