concept-scaffold-gen

Installation
SKILL.md

ConceptScaffoldGen

Scaffold a concept spec for $ARGUMENTS with annotations, state declarations (groups, enums, records), typed action signatures, capabilities, and a register() action.

When to use: Use when creating a new concept specification from scratch. Generates a .concept file with annotations (@version, @category, @visibility, @gate), purpose, state (with groups, enum types, record types), actions with typed variants, invariants, capabilities block, and a register() action following Jackson's methodology.

Design Principles

  • Singularity: Each concept serves exactly one purpose — if the purpose has 'and', it's two concepts.
  • Independence: A concept never references another concept's types or calls another concept's actions. Use type parameters and syncs.
  • Sufficiency & Necessity: Every state field is needed by at least one action. Every action serves the concept's purpose. No dead state.
  • Invariant Completeness: Use all six invariant constructs comprehensively: example (named tests), forall (quantified properties), always (state predicates), never (safety), eventually (liveness), action requires/ensures (contracts). Cover core purpose, error paths, constraints, state transitions, and boundary conditions. Aim for 2-5 invariants per concept.
  • Success is ok: The happy-path variant must always be named ok. Do not use domain-specific success names like created, configured, registered, updated. Domain context belongs in the output fields. Exception: actions with multiple distinct success outcomes that syncs need to distinguish (e.g., ok/miss for cache lookup, clean/conflicts for merge).
  • Description Quality: Every variant description must explain the outcome in domain terms — never echo the variant name ('Created.') or use vague text ('Failed.'). Error variants explain what went wrong; ok variants explain what is now true.

Step-by-Step Process

Step 1: Register Generator

Self-register with PluginRegistry so KindSystem can track ConceptConfig → ConceptSpec transformations. Registration is also handled automatically by register-generator-kinds.sync.

Examples: Register the concept scaffold generator

const result = await conceptScaffoldGenHandler.register({}, storage);

Step 2: Preview Changes

Dry-run the generation using Emitter content-addressing to classify each output file as new, changed, or unchanged. No files are written.

Arguments: $0 name (string), $1 typeParam (string), $2 purpose (string), $3 stateFields (statefield[]), $4 actions (actiondef[]), $5 version (int?), $6 gate (bool?), $7 capabilities (string[])

Step 3: Generate Concept Spec

Generate a well formed . concept file with state declarations , typed action signatures , variant returns , and a register ( ) action for PluginRegistry discovery .

Arguments: $0 name (string), $1 typeParam (string), $2 purpose (string), $3 stateFields (statefield[]), $4 actions (actiondef[]), $5 version (int?), $6 gate (bool?), $7 capabilities (string[])

Checklist:

  • Concept name is PascalCase?
  • Type parameter is a single capital letter?
  • Purpose block describes why, not what?
  • State fields use correct relation types (set, ->, option, list)?
  • Every action has at least one variant?
  • register() action is included for PluginRegistry?
  • Annotations (@category, @visibility) are present?
  • @version annotation included if this is a versioned spec?
  • State fields use enum types for fixed value sets?
  • State groups organize related fields?
  • Capabilities block present for generator/plugin concepts?
  • Variant descriptions explain outcomes, not just echo variant names?
  • All files written through Emitter (not directly to disk)?
  • Source provenance attached to each file?
  • Generation step recorded in GenerationPlan?

Examples: Generate a basic concept

clef scaffold concept --name User --actions create,update,delete

Generate with custom state

clef scaffold concept --name Article --param A --category domain

Generate with version and gate

clef scaffold concept --name Approval --version 2 --gate --capabilities search,export

Step 4: Edit the Concept Spec

Refine the generated .concept file: 1. Add annotations: @version(N) for versioning, @gate for async gates, @category("domain") for grouping, @visibility("public"|"internal"). 2. Write a purpose block explaining why the concept exists (not how it works). 3. Design state fields: sets (set T), mappings (T -> Type), option/list wrappers, enum types ({Active | Inactive | Pending}), record types ({key: String, value: String}), state groups for related fields. 4. Define actions with typed params and variant returns. All primitives: String, Int, Float, Bool, Bytes, DateTime, ID. 5. Write invariants comprehensively using all six constructs: example (named conformance tests), forall (quantified properties with given/in), always (state predicates), never (safety properties), eventually (liveness), action requires/ensures (contracts). Use property assertions (d.status = "complete") and when guard clauses. Aim for 2-5 invariants covering: core purpose, error paths, constraint enforcement, state transitions, boundary conditions. 6. Add capabilities block if the concept is a generator or plugin. 7. Add fixtures to every action. CRITICAL: error-case fixtures (names containing empty_, invalid_, duplicate_, missing_, bad_, etc.) MUST include an explicit -> error or -> <specific_variant> annotation. Omitting the arrow defaults to -> ok, generating wrong conformance tests. ok fixtures for non-creator actions need after chains to seed prerequisite data.

Step 5: Generate Tests from Invariants

After writing invariants, generate comprehensive tests using TestGen. Invoke /create-implementation --concept <Name> first to create the handler, then generate tests from invariants: 1. Run TestGen/generate (MCP: test_gen_generate) with concept_ref and language to compile all six invariant construct types into tests:

  • example blocks → conformance test vectors (1:1 after/then mapping)
  • forall blocks → property-based tests with generated domains
  • always blocks → stateful sequence tests checking state predicates
  • never blocks → violation-attempt tests trying to reach bad states
  • eventually blocks → bounded liveness tests
  • requires/ensures → contract-constrained PBT with pre/postconditions
  1. Review generated test files — they should NOT need manual editing for example/forall/always/never constructs. Contract tests (requires/ensures) may need manual generator tuning for complex domain types. 3. Run TestGen/coverage (MCP: test_gen_coverage) to verify all invariant constructs have generated tests. Check the construct_coverage breakdown — each kind should show covered > 0. 4. Run npx vitest run generated/tests/<concept>.* to verify generated tests pass. 5. If coverage gaps exist, add more invariants to the concept spec (go back to the edit step) and re-run TestGen/regenerate.

References

Supporting Materials

Quick Reference

Input Type Purpose
name String PascalCase concept name
typeParam String Type parameter letter (default: T)
purpose String Purpose description
category String Annotation category (domain, devtools, etc.)
version Int @version annotation number
gate Bool @gate annotation for async gates
stateFields list StateField State declarations (with group, enum, record support)
actions list ActionDef Action signatures with variants
capabilities list String Capabilities block entries
invariants list String Invariant steps

Anti-Patterns

Purpose describes implementation

Purpose block says how the concept works instead of why it exists.

Bad:

purpose {
  Store users in a Map<string, User> and provide CRUD operations
  via async handler methods.
}

Good:

purpose {
  Manage user identity and profile information.
}

Missing variants

Action only has ok variant — no error handling path.

Bad:

action create(name: String) {
  -> ok(user: U) { Created. }
}

Good:

action create(name: String) {
  -> ok(user: U) { New user registered and ready for profile setup. }
  -> duplicate(name: String) { A user with this name already exists. }
  -> error(message: String) { Creation failed due to a storage or validation error. }
}

Terse or echo descriptions

Variant descriptions that echo the variant name or use a single generic word — they tell the reader nothing about the actual outcome.

Bad:

action create(name: String) {
  -> ok(user: U) { Created. }
  -> error(message: String) { Failed. }
}

Good:

action create(name: String) {
  -> ok(user: U) { New user registered and ready for authentication setup. }
  -> error(message: String) { Creation failed due to a storage or validation error. }
}

Validation

Generate a concept scaffold:

npx tsx cli/src/index.ts scaffold concept --name User --actions create,update,delete

Validate generated concept:

npx tsx cli/src/index.ts check specs/app/user.concept

Generate tests from invariants:

npx tsx cli/src/index.ts test-gen --concept User --language typescript

Run generated invariant tests:

npx vitest run generated/tests/User.*

Check invariant coverage:

npx tsx cli/src/index.ts test-gen --coverage --concept User

Run scaffold generator tests:

npx vitest run tests/scaffold-generators.test.ts

Related Skills

Skill When to Use
/concept-designer Design concepts using Jackson's methodology before generating
/create-handler Generate handler implementations for the concept
/create-sync Generate sync rules connecting the concept
/test-gen Generate tests from invariants (TestGen/generate, TestGen/coverage)
/create-view-query For view-layer concepts, compose with QueryProgram/FilterSpec/SortSpec instead of ad-hoc data fetching
Related skills
Installs
10
First Seen
Mar 11, 2026
Security Audits