Init Config File
This skill is the
single authoritative writer of
for a ***plain project. Anything that ends up in
should go through this skill. The renderer (
) reads each key listed below into the same argparse namespace it uses for CLI flags — so a value set in
is exactly equivalent to passing the corresponding
on the command line.
When to run
- End of Phase 3 / start of Phase 4 — after every test-script decision is locked in (unit tests, conformance tests, prepare-environment), before delegating to .
- End of — only when Phase 3 of the feature touched the testing surface (new script generated, script removed, template directory introduced).
- End of any single-skill workflow that finalizes a script or template — e.g. after
implement-unit-testing-script
, implement-conformance-testing-script
, implement-prepare-environment-script
, , or .
- On demand — when the user asks "rebuild my config", "what valid keys are there", or you discover the config file is hand-edited / inconsistent.
If you only fixed a typo inside a
file and the testing surface didn't move, you do
not need to re-run this skill — go straight to
.
What this skill does
- Determines how many files the project needs (one per part — see Per-part split).
- For each config, gathers the decided values from the current project state (existing scripts under , the template directory, the build/dest folder choices).
- Emits a clean, alphabetically-grouped containing only keys that are actually in use, using the canonical key names from the Valid keys reference.
- Verifies that every value points at a file that exists on disk under (or wherever the user placed it), using the same lookup rule the renderer uses: absolute path → path relative to the config file's directory → path relative to the renderer's directory.
- Hands off to for the full validation pass.
What this skill does NOT do
- It does not generate testing scripts. Use
implement-unit-testing-script
, implement-conformance-testing-script
, or implement-prepare-environment-script
first; this skill only wires them in.
- It does not decide whether the user wants conformance tests, a prepare-environment script, copy-build, etc. Those decisions belong to Phase 3. This skill only records them.
- It does not invent values for keys whose decisions weren't made — it leaves them out (the renderer falls back to its default) rather than guessing.
- It does not write secrets. belongs in the environment variable, never in .
Valid keys reference
The canonical list of keys is derived from the
CLI argparse parser. Every key below corresponds to exactly one
and is read by the renderer through
.
No other keys are valid — the renderer rejects unknown keys with
parser.error(f"Invalid argument: {key}")
.
YAML keys use the
dashed form (e.g.
, not
) to mirror the CLI flag spelling. The only exception that has historically appeared with underscores is
; prefer
for new configs but accept either when reading an existing file.
Keys you typically include
These keys reflect choices made in Phase 3 of
and are the bread and butter of a project's
:
| Key | Type | Default | When to include |
|---|
| path (string) | — | Required. Every project gets a unit-test runner. Path resolves relative to the config file's directory (preferred) or the renderer directory. |
| path (string) | — | Include when the user opted into conformance testing in Phase 3. |
prepare-environment-script
| path (string) | — | Include only when both (a) the user opted into a prepare-environment script and (b) is also set. Setting prepare without conformance is a hard failure. |
| int (seconds) | | Include only when the user explicitly raised/lowered the default. |
| path (string) | — | Include whenever the project has an module or a custom template directory (e.g. ). Required for projects with shared templates. |
| path (string) | | Points at a separate YAML file consumed by Python's logging.config.dictConfig
. This is the only knob that lets the user actually change log levels for the renderer and its dependencies. See Logging configuration below. Include the key explicitly whenever the project ships a non-default logging config; leave it out only when the user is happy with the renderer's defaults ( root, for , for ). |
| string | | Include only when the user picked a non-default folder name. |
| string | | Include only when the user picked a non-default folder name. Must differ from . |
| string | | Always include with the value . This skill pins the copy destination explicitly so every project's has the same, predictable target folder for the post-render copy. Even though matches the renderer's default, we still write it out so the choice is visible in the file and protected against future default changes. Must differ from . |
| string | — | Include when the user wants build output rooted somewhere other than the project root. |
Keys you occasionally include
These are useful but the defaults are almost always fine. Only include them when the user explicitly changed the default during Phase 3:
| Key | Type | Default | Notes |
|---|
| bool | | The renderer copies the rendered code to after a successful render. Set to only when the user doesn't want this. |
| bool | | Requires to also be set. |
| string | | Target folder for the conformance-test copy. Must differ from . |
| bool | | Disable only when the user explicitly does not want a log file. Controls whether logs are mirrored to disk — it does not set the log level (that's 's job). |
| string | | If is , this key must be left out. Resolved relative to the file directory. |
| bool | | Include only when the user wants the state-machine graph rendered. |
| bool | | Include only when the project is meant to run in CI / non-interactive mode by default. |
| bool | | Almost never belongs in ; prefer the CLI flag for one-off forced renders. |
| bool | | Almost never belongs in ; prefer the CLI flag for one-off verbose runs. |
| URL (string) | | Include only when the user is pointing at a non-default API endpoint. |
Keys you must NEVER include
These flags are
per-invocation or
secret. Putting them in
is always wrong:
| Key | Why it doesn't belong |
|---|
| Secret. Belongs in the environment variable. Never in a file the user might commit. |
| Per-invocation. runs the dry-run explicitly; pinning it in the config would make a real render impossible. |
| Per-invocation preview. Mutually exclusive with . |
| Per-invocation. Selects a slice of functionalities to (re)render. |
| Per-invocation. Mutually exclusive with . |
| Internal / debugging flag. |
| Refers to the config file itself — the renderer ignores it when reading the config. |
| The file to render is always passed positionally on the CLI. |
If the user asks to put any of these in
, refuse and explain why.
Logging configuration
Log
levels are not controlled directly by
— they live in a separate YAML file that the renderer feeds to Python's
logging.config.dictConfig
. The
key
is the pointer that wires the two together.
How the renderer assembles logging
- The renderer first installs a set of baseline levels:
- root logger →
- (the renderer's own logger) →
- →
- →
transitions.extensions.diagrams
→
- If resolves to an existing file on disk, the renderer loads that YAML and calls
logging.config.dictConfig(...)
on it. This overlays anything from step 1 — the user can raise, lower, or add levels for any logger they care about, add handlers, change formatters, etc.
- The renderer then attaches its own handlers (TUI handler unless , file handler if , crash buffer otherwise). Whatever level the root logger ended up at after step 2 is the level those handlers respect.
In other words:
is the
only knob that changes the levels.
and
only control
whether and where logs are written — not
what gets written.
Default behavior
- The CLI default value for is . If a file by that exact name exists in the current working directory, it will be loaded automatically — even without being set in .
- If the file does not exist, the renderer silently keeps the baseline levels from step 1 above (no warning).
- If the file exists but fails to parse / apply, the renderer warns (
Failed to load logging configuration from …
) and falls back to the baseline.
This means
the mere presence of a file is itself a config decision. When you assemble a project's
, you have three cases:
| Situation | What to do |
|---|
| The user is happy with the baseline levels and the project has no on disk. | Leave out of . |
| The user wants custom levels and is fine with the file being named next to the file. | Create that file (see Recommended logging config below). Leaving out of is fine — the default points at it already — but explicitly setting logging-config-path: logging_config.yaml
is also acceptable and makes the dependency visible to anyone reading the config. |
| The user wants the logging config file to live somewhere non-default (different filename or directory). | Create the file at the chosen path and set logging-config-path: <that path>
in . |
When in doubt, ask the user: "Do you want to change the default log levels (INFO for the renderer, WARNING for git, ERROR for transitions), or stick with the defaults?" Only generate / pin the file when they say yes.
Recommended logging config
When the user does want custom levels, write a minimal
-style YAML file. Example with two common knobs (verbose renderer logs, plus suppression of a chatty third-party logger):
version: 1
disable_existing_loggers: false
formatters:
default:
format: "%(levelname)s:%(name)s:%(message)s"
handlers:
console:
class: logging.StreamHandler
formatter: default
level: DEBUG
loggers:
codeplain:
level: DEBUG
urllib3:
level: WARNING
root:
level: INFO
handlers: [console]
Guidelines for what to put in this file:
- Always set — requires it.
- Set
disable_existing_loggers: false
unless the user explicitly wants to silence loggers that were created before ran. The renderer creates several before it loads this file, and disabling them by default leads to confusing dead silence.
- Only override levels the user actually asked about. Don't preemptively add every logger the codebase touches — that creates ongoing maintenance for no benefit.
- Don't put the / / here — the renderer attaches those itself after runs. Adding them here will cause duplicate log lines.
- This file is not validated by . If you change it, ask the user to confirm by reading it back to them.
Per-part split
The rule, which mirrors what
Phase 3 already establishes, is
one per part of the system that has its own testing scripts:
- Single-stack project (e.g. one Python service) → one at the project root.
- Multi-part project (e.g. Python backend + React frontend) → one per part, placed next to the part's top module (e.g. , ). Each config references only its own scripts; never mix stacks in a single config.
- A part's split should follow the module boundaries from Phase 1 / Phase 2: if a module has its own language, framework, and test scripts, it gets its own next to that module.
Before emitting anything, state the planned split to the user (e.g. "I'll emit
and
") if there is more than one part.
Workflow
Step 1 — Inventory
- List every file in the repo and identify the top modules (modules not -ed by anything else) — same procedure as Step 1.
- For each top module, determine which part it belongs to (single-stack → one part; multi-part → one part per top module).
- List every script under and group them by part (e.g. belongs to the backend part, belongs to the frontend part).
- Identify the template directory (typically ) and any custom resource directories (typically ).
- Read any existing in each part's directory — preserve any user-set fields not listed in Valid keys reference only with the user's explicit approval, and warn that unknown keys will be rejected by the renderer.
Step 2 — Assemble per-part values
For each part:
- Start from an empty key set.
- Add
unittests-script: test_scripts/run_unittests_<lang>.<sh|ps1>
— required. If the script doesn't exist yet, stop and tell the caller to run implement-unit-testing-script
first.
- If the part has a conformance script on disk → add
conformance-tests-script: …
.
- If the part has a prepare-environment script on disk → first verify is also being added; if not, stop and surface this to the user (offer to either generate the missing conformance script via
implement-conformance-testing-script
or drop the prepare-environment script).
- If the project has shared templates → add (or whatever path the user used).
- Always add . This skill pins the copy destination on every config it writes, regardless of what Phase 3 said about it. If Phase 3 explicitly asked for a different , stop and surface the conflict to the user — do not silently honor the override.
- For every other key in Valid keys reference, include it only if Phase 3 produced a non-default decision for that key.
- Cross-validate the assembled key set:
- is set to .
- ≠ (in particular, is never ).
- ≠ .
copy-conformance-tests: true
requires .
- is set ⇒ is not .
- All paths resolve on disk (absolute → relative to config dir → relative to renderer dir).
- No script path crosses stacks (e.g. must not reference ).
Step 3 — Emit
For each part, write a clean YAML file:
- One key per line, in the order they appear in Valid keys reference (script paths first, then template/build folders, then copy/log settings).
- Use dashed key names. Quote string values only when YAML requires it.
- No comments inside the file — keep it machine-parseable. If the user needs a comment, put it in the surrounding spec or README.
- Idempotent: re-running this skill on an unchanged project produces a byte-for-byte identical file.
Example for a single-stack Python project with conformance testing and a prepare-environment script:
unittests-script: test_scripts/run_unittests_python.sh
conformance-tests-script: test_scripts/run_conformance_tests_python.sh
prepare-environment-script: test_scripts/prepare_environment_python.sh
template-dir: template
build-dest: dist
Example for a multi-part project (
):
unittests-script: test_scripts/run_unittests_python.sh
conformance-tests-script: test_scripts/run_conformance_tests_python.sh
template-dir: ../template
build-dest: dist
Step 4 — Hand off
Tell the caller exactly which file(s) were written and invoke
to validate the project end-to-end. Do
not declare success on your own —
is the source of truth for "is this project ready to render?".
Anti-patterns
- Inventing values for keys the user never decided on. Leave them out and let the renderer use its default.
- Mixing stacks in one config. referencing a JS script is always a bug — split into per-part configs instead.
- Putting , , , , , , , or in . All of these are per-invocation or secret; the renderer treats them as command-line concerns.
- Emitting
prepare-environment-script
without . A prepare-environment script only makes sense in service of conformance tests; without one, will fail.
- Hand-merging into an existing config.yaml without re-running this skill. If the user edited the config manually, re-run the skill to re-derive a clean canonical version (after confirming any custom fields with the user).
- Reading the renderer's API key from a project file. Always rely on .
Validation Checklist