## Table Of Contents 1. [Explanations](#1-explanations) 2. [System Architecture](#2-system-architecture) 1. [System Model](#system-model) 2. [Package Layout](#package-layout) 3. [Prompt And Resource Ownership](#prompt-and-resource-ownership) 3. [Configuration And Dependency Model](#3-configuration-and-dependency-model) 1. [Configuration Model](#configuration-model) 2. [Dependency Model](#dependency-model) 4. [Workflow Models](#4-workflow-models) 1. [Manifest-To-Export Model](#manifest-to-export-model) 2. [Caption Sidecar Model](#caption-sidecar-model) 3. [LoRA Workflow Model](#lora-workflow-model) 4. [Staged T2I Pipeline Model](#staged-t2i-pipeline-model) 5. [Image Utility Model](#image-utility-model) 5. [Failure Model](#5-failure-model)
# 1. Explanations Use this file when you need to understand why `kneifftools` is shaped the way it is. Use [How-To User Guides](How-To-User-Guides.md) for commands and [References](References.md) for exact names.
# 2. System Architecture ## System Model `kneifftools` is a src-layout Python project. Runtime code lives under [src/kneiff](../src/kneiff), maintainer-only helpers live under [src/kneiff_dev](../src/kneiff_dev), tests live under [tests](../tests), and long-form documentation lives under [docs](.). The main idea is simple: 1. `pyproject.toml` describes the package, dependencies, tools, and entry points. 2. `src/kneiff` owns importable runtime behavior. 3. `tests` verifies behavior from the outside where possible. 4. `docs` explains setup, workflows, exact names, and design context. 5. `justfile` provides repeatable maintainer commands. > [!NOTE] > Related links: > - Use [install the package](How-To-User-Guides.md#install-the-package) for the first setup path. > - Use [project paths](References.md#project-paths) for exact source owners. > - Use [repository routing](Development.md#repository-routing) before moving behavior.
## Package Layout The project uses a `src` layout so imports come from the installed package rather than accidentally from the repository root. Core boundaries: | Area | Responsibility | |---|---| | `src/kneiff` | Runtime behavior and package-owned helpers. | | `src/kneiff_dev` | Maintainer-only helpers. | | `tests` | Behavior checks, fixtures, and regression tests. | | `docs` | Longer usage, reference, and architecture material. | | `docs/assets` | Tracked generated documentation figures. | | `assets/*.IGNORE*` | Ignored local diagram scratch files. | | `experiments/` | Ignored local research and one-off probes. | Keep broadly reusable helpers near the package area that owns the domain. Keep one-off diagnostics in the nearest existing tooling, test, or experiment area. > [!NOTE] > Related: use [Development: source editing rules](Development.md#source-editing-rules) > for implementation rules that preserve these boundaries.
## Prompt And Resource Ownership Prompt catalog parsing does not select a project or import an inference or training backend. The dependency direction is deliberately one-way: ```text project_resources ──> prompts.catalog │ └───────────> comfy.workflow_presets ──> prompts.catalog training.lora ──────> project_resources + prompts.catalog comfy.lora ─────────> no prompt or showcase modules ``` `kneiff.prompts.catalog.load_prompt_catalog()` receives a vocabulary and an optional overlay path. `kneiff.project_resources.load_project_prompt_catalog()` owns project path validation and vocabulary loading. SimpleTuner artifact builders receive a resolved catalog instead of reaching back into project selection. Generic ComfyUI LoRA resolution remains independent of showcase request types so other pipelines can reuse it without importing prompt code.
# 3. Configuration And Dependency Model ## Configuration Model Kneiff uses explicit files for workflow state: - The AppRC application name is `knf`. `~/.config/knf/knf.apprc.toml` owns its registry of persistent storage roots; `KNF_APPRC_TOML` can point Kneiff at a different registry path. - `~/.config/knf/.env.apprc-app` owns app-wide Kneiff overrides. - `vocabulary.knf.yaml` owns project identity tokens, controlled caption axes, and project-specific vocabulary layered over Kneiff's reusable JTP axes. - Kneiff's packaged core prompt catalog owns reusable prompt defaults, `character_reference`, and fixed validation controls. `prompts.knf.yaml` is the optional project overlay for scenes and project-specific prompt choices. The resolved catalog routes rows through explicit `uses`; Kneiff does not infer showcase eligibility. - `workflows/showcase_.workflow.json` optionally overrides a packaged showcase workflow for one project. - `configs/*.knf.yaml` owns export and optional SimpleTuner training settings. `knf project init` always creates `configs/ANIMA.knf.yaml`, the Sygred Anima training template with local component paths, and `configs/F2K_9B.knf.yaml`, the Flux2 Klein 9B template using the official base repository and unset component paths. The Flux2 config uses Sygred's 320-step profile with a fullbody/genitals warm-up; the Anima config retains its small-run settings. - `SOURCE/` owns source images scanned by dataset sync. A new project includes `0-FULLBODY/` and `2-HEAD/`; add an anatomy-focused folder only when a project config maps it. - `MANIFEST.knf.xlsx` owns the editable dataset index with paths relative to `SOURCE/`; generated `MANIFEST.yaml` sits beside it for Git review. - `HF//` owns public Hugging Face dataset exports. The root `HF/` directory is created empty by the scaffold and ignored by Git. - `TRAINING/_/` owns SimpleTuner run state. The root `TRAINING/` directory is created empty by the scaffold and ignored by Git. - `.old_manifests/` owns archived manifest workbooks. - `.env.apprc-storage` owns machine-local AppRC overrides such as `KNF_WORKERS`, `COMFY_MODELS_DIR` for ComfyUI showcase LoRA discovery, `COMFY_LORAS_DIR_1` for the interactive picker start directory, and `COMFY_UPSCALE_MODEL` for server-side ComfyUI upscaling. - `.git/`, `.gitignore`, and `.gitattributes` own the new project's local Git repository and generic ignore/LFS tracking policy. The scaffold initializes a `main` branch and local Git user name only for a new repository; it never creates a commit, configures a remote or email, alters global Git settings, or installs Git LFS. - `default_tags.txt` is a project-owned seed containing `kneiff` plus the project's activation and species tokens. It is intentionally not runtime input to Kneiff. - `.env` owns local OpenAI-compatible caption server settings. Resource ownership follows semantic scope, not which project first needed a resource. A caption tag or model-family workflow stays packaged when unrelated projects can reuse its meaning unchanged. Identity tokens, project-only axes, curated prompts, and workflows containing project-specific choices belong in the selected storage root. The default Anima config follows the same boundary: it uses a generic model/training recipe and packaged validation controls, while the root vocabulary and optional prompt overlay supply project identity and scenes. No Rook prompt, identity, local model path, tracker, or publishing setting is copied into a new project. AppRC constructs one typed `KneiffConfig` bundle from native `StorageConfig`, `ComfyConfig`, `LmStudioConfig`, and `PromptgenConfig` sections. Runtimeful commands require an explicit storage selector through `KNF_STORAGE` or the root `--storage` option. A lone registry entry is not selected implicitly. The selected AppRC storage root is the project boundary, and Kneiff derives every project path from the fixed scaffold above. `.env.apprc-storage` cannot select that root because AppRC must resolve the selector before it knows which local file to load. `knf project use` writes an app-wide fallback; shell `KNF_STORAGE` and root `--storage` override it. Kneiff does not use a second project descriptor such as `kneiff.project.yaml`. Such a file would duplicate the storage selector and fixed path convention without adding a separate source of truth. Explicit config paths must remain inside the selected project root. Rejecting cross-project paths prevents one project's config from silently using another project's vocabulary or prompts. When configuration affects a user-visible workflow, update [How-To User Guides](How-To-User-Guides.md) and [References](References.md) together. > [!NOTE] > Related: use [environment variables](References.md#environment-variables) for > exact variable names and [configuration files](References.md#configuration-files) > for file owners.
## Dependency Model The project separates dependency types by audience: | Dependency Type | Owner | Audience | |---|---|---| | Runtime dependency | `[project].dependencies` | Users who install the package. | | Dependency group | `[dependency-groups]` | Maintainers who run tests, typing, linting, docs, or profiling. | | Lock file | `uv.lock` | Maintainers or CI jobs that want reproducible `uv` syncs. | This split keeps normal installs usable without requiring `uv`, while leaving maintainer workflows repeatable. > [!NOTE] > Related links: > - Use [install the package](How-To-User-Guides.md#install-the-package) for install commands. > - Use [dependency surfaces](References.md#dependency-surfaces) for exact `pyproject.toml` sections.
# 4. Workflow Models ## Manifest-To-Export Model Kneiff treats the source image folder as the source of truth and the export folder as disposable output. After AppRC selects a storage root, config discovery starts from that root. A config selector such as `chroma` matches files like `configs/CHROMA-2.knf.yaml` case-insensitively and only auto-selects when the result is unambiguous. Absolute config paths are valid selection inputs, but do not bypass the required AppRC storage root and must stay inside it. The dataset path is: 1. `knf dataset sync configs/example.knf.yaml` or `knf dataset sync example` scans image files under `SOURCE/`. 2. `MANIFEST.knf.xlsx` stores one fixed 13-row block per image. Column C owns user input; the merged columns D-H show `tag`, `json`, `nlg`, `chroma`, and `prose` renderings from one shared Kneifftags analysis. 3. Sync rewrites schema-version-2 `MANIFEST.yaml` with logical identity and user input but no derived captions. 4. Manual annotation fills the fixed semantic fields and the `SFW` control. 5. `configs/*.knf.yaml` maps `SOURCE`-relative folders to export subsets. 6. Export planning selects rows, reuses their analysis, optionally resizes images, and writes a public dataset under `HF//`. The config filename selects the dataset id; config content does not own paths. Kneiff rejects `source_root`, `manifest_path`, `export_root`, and `allow_export_inside_source` so a config cannot redirect reads or writes outside the selected AppRC storage. Dataset sync is dataset-only. LoRA training state is generated later by `knf train prepare` or fresh `knf train start` under numbered `TRAINING/_/` workspaces, so manifest sync does not need underscore-prefixed output guards. > [!NOTE] > Related links: > - Use [sync a manifest workbook](How-To-User-Guides.md#sync-a-manifest-workbook) for the command sequence. > - Use [configuration files](References.md#configuration-files) for exact file names.
## Caption Sidecar Model Captions are rendered from the structured values in each fixed image block. This gives workbook previews, validation prompts, ComfyUI prompts, and export sidecars one semantic source. The main pieces are: | Piece | Role | |---|---| | Kneifftags built-in vocabulary | Owns reusable categories, groups, aliases, conflicts, output tags, and profile prose. | | `vocabulary.knf.yaml` | Strict schema-2 extension for project identities and project-only vocabulary. | | `caption_outputs` | Sets global sidecar mode, formats, and tag scope. | | `caption_outputs_overrides` | Changes sidecar output behavior for one subset. | | `caption` | Supplies an optional project-wide `subject_sex` fallback. | Kneifftools gathers the fields in block order, omits `SFW` and `Notes`, and calls the selected project engine once. The immutable analysis contains resolved definitions, category membership, unknown inputs, ambiguities, conflicts, and diagnostics. All output profiles reuse that result; no consumer parses the row a second time. Unknown tags are preserved through Kneifftags fallback rendering. Warnings do not discard input, while active engine errors block activation or export. A row marked `SFW` receives an additional application-level safety check because `SFW` is workflow control rather than caption content. The project extension is composed with the packaged Kneifftags vocabulary in an isolated engine. Character and species defaults use namespace-qualified inputs to prevent collisions with built-in spellings. Kneifftools reads vocabulary definitions through the public root API for prompt and field catalogs; it does not inspect Kneifftags internals. `hybrid_txt` writes one `.txt` sidecar for each image variant. `separate_txt` writes separate sidecars for selected formats. The canonical format names are `tags`, `natural`, `json`, `nlg`, `chroma`, and `hybrid`. Removed `tag` and `prose` aliases are rejected at the config and prompt-catalog boundaries. `tag_scope: supplemental` omits tag facts already expressed by natural text inside a joined caption; `tag_scope: all` keeps the complete tag tail. Standalone `tags` and `chroma` files use all tags. Mirrored Chroma sidecars receive the application-owned `variant.` prefix. Caption-text compatibility does not make LoRA weights cross-model-compatible. > [!NOTE] > Related: use [configure caption outputs](How-To-User-Guides.md#configure-caption-outputs) > for the config shape.
## LoRA Workflow Model LoRA training is layered on top of dataset sync. The same `configs/*.knf.yaml` file owns both the export plan and the optional `training.simpletuner` block. The LoRA path is: 1. Resolve the exported dataset root from `HF//`. This root must already be populated by `knf dataset sync`; training commands validate it rather than creating or refreshing it. 2. Resolve the run workspace as `TRAINING/_/`; fresh prepare or start commands choose the next free run number. 3. Copy configured training subsets into the run workspace's `dataset/` so SimpleTuner can write bucket metadata without touching public subset folders. 4. Translate `training.simpletuner` into SimpleTuner JSON artifacts under the run workspace and write `kneiff-training-run.json` as `not_started`. Kneiff owns fixed names for the trainer JSON, backend JSON, optional validation prompt JSON, cache directory, and output directory. 5. Review the generated artifacts before launch, or resume an existing `not_started` or `incomplete` run with `knf train start --resume`. 6. Resolve the interpreter declared by the installed `simpletuner` tool, then launch Kneiff's copied patch runner in an isolated child process with environment variables prepared for the selected CUDA device. Update the run state to `running`, `failed`, `incomplete`, or `complete` without importing SimpleTuner or mutating process-global state in Kneiff. 7. After training, convert Chroma and Anima LoRA checkpoints to ComfyUI-native module names when the trainer asks for ComfyUI format and write the final validation contact sheet as `TRAINING/_-kneiff-validation-progress-grid.jpg`. A numbered workspace is valid only with its generated `kneiff-training-run.json` at schema version 1. Persisted paths must be normalized absolute paths that match the current project and remain below that workspace. Persisted subset names must exactly match the direct dataset child directories referenced by active image backends. Before launch, Kneiff verifies the stored hashes and validates paths and optional prompt content from the generated JSON itself. Run state retains only metadata deliberately excluded from the parser JSON; it cannot override disk-owned fields. Kneiff rejects old, edited, symlinked, or external state instead of guessing paths and rewriting generated files. The conversion code fuses split Diffusers-style attention LoRA tensors into ComfyUI's fused Chroma layer names and renames Diffusers-style Anima attention adapters to ComfyUI's native Anima blocks while preserving tensor data. > [!NOTE] > Related: use [LoRA workflows](How-To-User-Guides.md#4-lora-workflows) for the > commands and [public interfaces](References.md#public-interfaces) for config names.
## Staged T2I Pipeline Model Solo and duo are presets over one staged runner. The runner owns job planning, coordinate-derived seeds, queue ordering, downloads, cancellation, and the run manifest. The public `t2i_solo.run()` and `t2i_duo.run()` functions only select the allowed preset shape. Prompt generation and image generation overlap deliberately. After LM Studio reviews one variation, all baseline branches for that variation are queued before the next LM request starts. Duo cleanup is a barrier stage: it begins only after every Anima baseline has completed. That ordering lets ComfyUI use one generation model stage followed by one Flux2 edit stage instead of alternating large models. The root seed identifies an image experiment, but it does not seed LM Studio. Each image branch hashes the root seed with its stage and prompt, image, and cleanup coordinates. Adding later branches therefore cannot renumber earlier seeds. The manifest preserves sampled raw LM responses and the effective prompts after exact activation-token repair. Participant identity remains project-owned. Slot 1 is the canonical `custom_tokens.character` value; `additional_activation_tokens` supplies later slots in order. CLI activation-token values affect only one run. They can override a vocabulary slot or fill a missing slot with a warning, but the runner never persists them. Cancellation is based on positive ownership. Each run has a unique ComfyUI `client_id` and retains every prompt id returned for its jobs. Ctrl-C deletes only those known ids from the pending queue. It interrupts a running prompt only after the current queue state confirms that the active id belongs to the same set. The servers and unrelated OS processes are never stopped. `run.knf.yaml` is a provenance and partial-failure record, not a checkpoint. Atomic replacement keeps it readable after process failure, while the absence of resume support avoids treating server queue state and local partial files as a recoverable transaction.
## Image Utility Model Image utilities are CLI wrappers around focused package modules: | Command Group | Model | |---|---| | `png2jpg` | Batch convert PNG files into JPEG files under an output subdirectory. | | `concat` | Build quick horizontal contact sheets. | | `rename` | Apply predictable file names before dataset sync. | | `upscale` | Run image upscaling with selectable model weights and output settings. | | `tag` | Run RedRocket/JTP-3 tagging over files or directories. | | `caption` | Generate captions through an OpenAI-compatible server or BLIP/Qwen path. | These utilities are deliberately narrow. Dataset structure and training sidecar policy still belong to `knf dataset`, not the image commands. > [!NOTE] > Related: use [image workflows](How-To-User-Guides.md#5-image-workflows) for > command examples.
# 5. Failure Model Most failures become easier to debug when checked in this order: 1. Confirm whether the command expects the Kneiff repository root or the AppRC-selected project root. 2. Confirm the active Python executable. 3. Confirm `kneiff` imports from the editable install. 4. Confirm dependencies are installed for the workflow. 5. Confirm the command is documented in [References](References.md). 6. Confirm the expected config files and environment variables exist. 7. Re-run the smallest command that reproduces the problem. ```bash pwd python -c "import sys; print(sys.executable)" python -c "import kneiff; print(kneiff.__file__)" knf --help ``` Dataset-specific failures usually narrow down to one of these: | Symptom | First Check | |---|---| | Missing rows | Confirm `Relative_path` and workbook sheets after `knf dataset sync configs/example.knf.yaml --manifest-only`. | | Export validation error | Run `knf dataset sync configs/example.knf.yaml --dry-run`. | | Missing captions | Check populated manual columns and the active `caption_outputs` config. | | Caption server error | Check `.env`, `BASE_URL`, and `OPENAI_API_KEY`. | | LoRA export or launch error | Run `knf dataset sync configs/example.knf.yaml`, prepare a new run, then check generated SimpleTuner artifacts and CUDA device options. | > [!NOTE] > Related links: > - Use [environment problems](How-To-User-Guides.md#environment-problems) for import and interpreter checks. > - Use [command problems](How-To-User-Guides.md#command-problems) when a recipe fails. > - Use [command reference](References.md#command-reference) for the expected command names.