Business Rules¶
BR-001: Non-Empty Project Name¶
Category: Policy
Statement: A project name must not be blank.
Rationale: The name is used in generated READMEs, CI/CD files, and MkDocs configuration; a blank name produces broken output.
Applies to: GenerationRequest
Violated when: --name is absent or contains only whitespace.
Error code: EMPTY_PROJECT_NAME — "project name must not be empty"
| Valid | Invalid |
|---|---|
My Project |
"" |
repo |
" " |
BR-002: Non-Empty Project Description¶
Category: Policy
Statement: A project description must not be blank.
Rationale: The description appears in generated READMEs and MkDocs site metadata; a blank value produces incomplete documentation.
Applies to: GenerationRequest
Violated when: --description is absent or contains only whitespace.
Error code: EMPTY_PROJECT_DESCRIPTION — "project description must not be empty"
| Valid | Invalid |
|---|---|
"Scaffolds new projects." |
"" |
BR-003: Non-Empty Target Path¶
Category: Policy
Statement: The target path must not be blank.
Rationale: The generator writes all output into the target path; without it no file can be created.
Applies to: GenerationRequest
Violated when: --targetpath is absent or contains only whitespace.
Error code: EMPTY_TARGET_PATH — "target path must not be empty"
| Valid | Invalid |
|---|---|
/home/user/repo |
"" |
./my-repo |
" " |
BR-004: Valid Language¶
Category: Policy
Statement: The target language must be one of cpp, go, java, or typescript.
Rationale: The generator only has template sets for these four languages.
Applies to: GenerationRequest
Violated when: --language is any value not in {cpp, go, java, typescript}.
Error code: INVALID_LANGUAGE — "invalid language: must be one of cpp, go, java, typescript"
| Valid | Invalid |
|---|---|
go |
python |
cpp |
rust |
java |
"" |
typescript |
BR-005: Valid Project Type¶
Category: Policy
Statement: The project type must be one of embedded, library, service, tool, or
frontend-react. embedded is reserved for a planned future C++ project type (e.g. STM32
targets) and has no template set yet — see BR-006/BR-007 for currently supported combinations.
frontend-react is TypeScript-only.
Rationale: The generator has template sets for library, service, tool, and
frontend-react; embedded is documented ahead of implementation.
Applies to: GenerationRequest
Violated when: --projecttype is any value not in
{embedded, library, service, tool, frontend-react}.
Error code: INVALID_PROJECT_TYPE — "invalid type: must be one of embedded, library, service, tool, frontend-react"
| Valid | Invalid |
|---|---|
service |
plugin |
library |
framework |
frontend-react |
BR-006: Go Does Not Support Embedded Projects¶
Category: Invariant
Statement: The combination of language go and project type embedded is forbidden.
Rationale: Embedded and systems-level development in this domain targets C++ toolchains (AVR, ARM, etc.); Go has no applicable template set for this context.
Applies to: ProjectDescriptor
Violated when: Language is go AND ProjectType is embedded.
Error code: UNSUPPORTED_COMBINATION — "Go does not support embedded projects"
| Valid | Invalid |
|---|---|
go + service |
go + embedded |
cpp + embedded |
BR-007: Java Supports Only Library and Service Projects¶
Category: Invariant
Statement: The language java is restricted to project types library and service.
Rationale: CLI tool and embedded templates do not exist for Java in this generator; generating them would produce incomplete scaffolding.
Applies to: ProjectDescriptor
Violated when: Language is java AND ProjectType is embedded or tool.
Error code: UNSUPPORTED_COMBINATION — "Java supports only library and service projects"
| Valid | Invalid |
|---|---|
java + library |
java + tool |
java + service |
java + embedded |
BR-008: CI Type Is Inferred from Git Remote When Not Specified¶
Category: Policy
Statement: When --ci is not provided, the CI type is derived from the Git remote URL of the current repository.
Rationale: Avoids manual input for the common case where the remote URL unambiguously identifies the CI platform.
Applies to: GenerationRequest
Violated when: --ci is absent AND the Git remote is unreachable, missing, or does not contain github.com or gitlab.com.
Error code: CI_TYPE_UNRESOLVABLE — "could not determine CI type: pass --ci github or --ci gitlab explicitly"
| Valid | Invalid |
|---|---|
Remote URL contains github.com |
No Git remote configured |
Remote URL contains gitlab.com |
Remote URL from unknown host |
--ci github passed explicitly |
BR-009: Artifact Name Is Inferred from Git Remote When Not Specified¶
Category: Policy
Statement: When --artifactname is not provided, it is derived as the last path segment of the normalised Git remote URL.
Rationale: The remote repository slug is the natural artifact name, avoiding redundant manual input.
Applies to: GenerationRequest
Violated when: --artifactname is absent AND the Git remote is unreachable or unparseable.
Error code: ARTIFACT_NAME_UNRESOLVABLE — "could not determine technical artifact name: pass --artifactname explicitly"
| Valid | Invalid |
|---|---|
Remote gitlab.com/user/my-tool → my-tool |
No Git remote configured |
--artifactname my-tool passed explicitly |
BR-010: Go Module Path Is Inferred from Git Remote When Not Specified¶
Category: Policy
Statement: When --modulepath is not provided, it is derived as the normalised host/org/repo path of the Git remote URL.
Rationale: The remote URL is the canonical Go module path for hosted repositories, avoiding redundant manual input.
Applies to: GenerationRequest
Violated when: --modulepath is absent AND the Git remote is unreachable or unparseable.
Error code: MODULE_PATH_UNRESOLVABLE — "could not determine Go module path: pass --modulepath explicitly"
| Valid | Invalid |
|---|---|
Remote git@gitlab.com:user/proj.git -> gitlab.com/user/proj |
No Git remote configured |
--modulepath gitlab.com/user/proj passed explicitly |
BR-011: Universal Guidelines Are Always Generated¶
Category: Invariant
Statement: Every generated project must include the full set of universal guidelines regardless of language or project type.
Rationale: These guidelines encode the non-negotiable quality baseline for all projects produced by this generator.
Applies to: GeneratedProject
Violated when: Any of bash_coding, ci_cd, data_modeling, error_handling, general_coding, library_design, refactoring, security, or testing is absent from the output.
Error code: MISSING_UNIVERSAL_GUIDELINE — "universal guideline missing from output"
| Valid | Invalid |
|---|---|
All 9 guidelines present in specs/guidelines/ |
Any guideline missing from output |
BR-012: C++ Artifact Names Are Sanitised to Valid Identifiers¶
Category: Invariant
Statement: Hyphens in the artifact name are replaced with underscores when used as a C++ namespace or include-directory name.
Rationale: Hyphens are not valid in C++ identifiers; using them verbatim would produce uncompilable code.
Applies to: ProjectDescriptor (C++ projects only)
Violated when: A C++ namespace or include path contains a hyphen character.
Error code: INVALID_CPP_NAMESPACE — "C++ namespace must not contain hyphens"
| Valid | Invalid |
|---|---|
artifact my-lib → namespace my_lib |
namespace my-lib |
artifact mylib → namespace mylib |
BR-013: Coverage Reports¶
Category: Invariant
Statement: All coverage reports, independent of programming language or artifact type, are stored in test_reports/coverage/. Coverage reports are in Cobertura format.
Rationale: Always clear where to find reports. Prominent format used by many tools. Folder can be provided for Download as a CI build artifact.
Applies to: All generated projects.
Violated when: never
Error code: not applicable
BR-015: Service Guidelines Are Generated for Service Projects¶
Category: Invariant
Statement: Every generated service project must include the full set of service-specific guidelines in addition to the universal guidelines.
Rationale: These guidelines encode quality standards that are specific to services — API contracts, process design, logging, monitoring, and cloud-native design — and are not applicable to libraries or CLI tools.
Applies to: GeneratedProject (project type service)
Violated when: Any of api_design, business_process_design, logging, monitoring, or service_design is absent from the output of a service project.
Error code: MISSING_SERVICE_GUIDELINE — "service guideline missing from output"
| Valid | Invalid |
|---|---|
All 5 service guidelines present in specs/guidelines/ |
Any guideline missing from service output |
BR-014: Unit Test Reports¶
Category: Invariant
Statement: All unit test reports, independent of programming language or artifact type, are stored in test_reports/test/. Reports are in JUnit XML format.
Rationale: Always clear where to find reports. Prominent format used by many tools. Folder can be provided for Download as a CI build artifact.
Applies to: All generated projects.
Violated when: never
Error code: not applicable
BR-016: No-Scaffolding Skips CI, Build Tooling, and Prescriptive ADRs¶
Category: Invariant
Statement: When --no-scaffolding is set, the generated project must include the full set of guidelines and AI-assistant support, but must not include any CI workflow file, build-tool file (Makefile, language build configuration), language scaffold tree, or prescriptive ADR — a single worked ADR example is generated instead.
Rationale: Lets a developer adopt this generator's guidelines and Claude Code integration while choosing their own CI system and build tool, without receiving files that presume a specific choice.
Applies to: GeneratedProject (when --no-scaffolding/-s is set)
Violated when: Any CI workflow file, Makefile, language scaffold file, or a prescriptive ADR is present in the output, or any guideline or the sample ADR is missing.
Error code: not applicable
| Valid | Invalid |
|---|---|
--no-scaffolding output has guidelines + .claude/ + ADR-001-example-decision.md, no Makefile/.gitlab-ci.yml |
--no-scaffolding output still contains Makefile or .gitlab-ci.yml |
BR-017: Container Registry Is Inferred from Module Path and CI Type When Not Specified¶
Category: Policy
Statement: When --containerregistry is not provided, it is derived from the Go module path's host/org segment and the CI type: ghcr.io/<org> for GitHub, registry.gitlab.com/<org> for GitLab.
Rationale: The module path's org segment is each platform's natural built-in registry namespace. This avoids redundant manual input for the common case.
Applies to: GenerationRequest
Violated when: never.
Unlike BR-008/BR-009/BR-010, an unresolvable module path does not fail generation.
It falls back to the placeholder registry.example.com/TODO for the developer to replace later.
Error code: not applicable
| Input | Resulting registry |
|---|---|
Module path gitlab.com/hzahnlei/my-project, --ci gitlab |
registry.gitlab.com/hzahnlei |
Module path github.com/myorg/my-project, --ci github |
ghcr.io/myorg |
--containerregistry registry.example.com/team passed explicitly |
registry.example.com/team |
No module path resolvable and no --containerregistry passed |
registry.example.com/TODO |
BR-018: Mono-Repo Manifest Must Contain At Least One Repo Entry¶
Category: Invariant
Statement: A .mono-repo.yaml manifest's repos: list must contain at least one entry.
Rationale: A mono-repo manifest with zero entries would generate nothing, which is never a
valid outcome for repo init.
Applies to: MonoRepoConfig
Violated when: repos: is absent, null, or an empty list.
Error code: EMPTY_MONO_REPO_MANIFEST — "mono-repo manifest must contain at least one repo entry"
| Valid | Invalid |
|---|---|
repos: with 1+ entries |
repos: [] |
repos: key absent |
BR-019: Each Repo Entry Is Validated Like a Single-Repo Spec¶
Category: Policy
Statement: Every entry in a .mono-repo.yaml manifest's repos: list is validated with the
exact same rules as a standalone .repo.yaml: non-empty name (BR-001), non-empty description
(BR-002), valid language (BR-004), valid project type (BR-005), and valid language/project-type
combination (BR-006, BR-007).
Rationale: A manifest entry is structurally a RepoConfig (plus authors); reusing the
same validation avoids a second, divergent set of rules for what is otherwise the same data
shape.
Applies to: Each item of MonoRepoConfig.Repos
Violated when: Any entry fails a rule it would fail as a standalone .repo.yaml (BR-001,
BR-002, BR-004, BR-005, BR-006, or BR-007).
Error code: same error code as the violated single-repo rule, prefixed with the entry's index or artifact name so the offending entry is identifiable
| Valid | Invalid |
|---|---|
Entry with language: java, type: service, non-empty name/description.brief |
Entry with language: go, type: embedded (BR-006) |
Entry with blank name (BR-001) |
BR-020: Artifact Names Must Be Unique Within a Mono-Repo Manifest¶
Category: Invariant
Statement: The artifact.name of every entry in a .mono-repo.yaml manifest's repos:
list must be unique within that manifest.
Rationale: Each entry's artifact.name becomes its target subdirectory name directly under
the mono-repo root; a duplicate would cause two entries to generate into the same directory.
Applies to: MonoRepoConfig
Violated when: Two or more entries share the same artifact.name.
Error code: DUPLICATE_ARTIFACT_NAME — "duplicate artifact name in mono-repo manifest"
| Valid | Invalid |
|---|---|
flight-booking, trip-booking-frontend |
flight-booking, flight-booking |
BR-021: Mono-Repo Generation Refuses Existing Target Subdirectories¶
Category: Invariant
Statement: For each entry in a .mono-repo.yaml manifest, generation refuses to run if
<targetpath>/<artifact.name> already exists.
Rationale: Mirrors the existing single-repo guard (GEN-S03 in cmd/init.go) that refuses to
overwrite an already-populated directory; applied per entry since each entry generates into its
own sibling subdirectory.
Applies to: Each item of MonoRepoConfig.Repos
Violated when: The subdirectory named after an entry's artifact.name already exists under
--targetpath.
Error code: TARGET_SUBDIRECTORY_EXISTS — "target subdirectory already exists: remove it or
run repo update there instead"
| Valid | Invalid |
|---|---|
<targetpath>/flight-booking does not yet exist |
<targetpath>/flight-booking already exists |
BR-022: Mono-Repo Children Use Artifact-Scoped CI Namespacing¶
Category: Invariant
Statement: For each entry in a .mono-repo.yaml manifest, the generated CI configuration
uses an artifact-scoped namespace instead of the bare form a standalone repo would use: the
registry image path gains a /<artifact.name> segment, and the release Git tag pattern becomes
<artifact.name>-vX.Y.Z. The changelog file itself stays a plain CHANGELOG.md — the artifact's
own subdirectory already disambiguates it — but every changelog CI job command (git cliff -o,
git add, commit, artifacts: paths:) is scoped with a <artifact.name>/ working-directory
prefix, the same pattern used for every other job, and git cliff itself is additionally scoped
via --include-path <artifact.name>/**. A standalone (non-mono) repo keeps today's unprefixed
forms ($CI_REGISTRY_IMAGE/ghcr.io/<repo>, vX.Y.Z) unchanged; it always had a plain
CHANGELOG.md at its own root, mono-repo or not.
Rationale: Without artifact-scoped namespacing, every entry in a mono-repo would compute the
identical registry push target and match the identical release tag pattern, silently colliding.
The registry/tag namespacing mirrors the convention already proven in the build-images
repository (<image>-vX.Y.Z tags); the changelog, by contrast, does not mirror
build-images' CHANGELOG-<image>.md naming — build-images is a flat, non-mono-repo layout
where that suffix disambiguates, whereas each mono-repo artifact here already has its own
subdirectory, making a suffix redundant (see ADR-014).
Applies to: Each item of MonoRepoConfig.Repos, at CI-file generation time
Violated when: Two or more entries' generated CI configuration resolve to the same registry image path or the same release tag pattern.
Error code: not applicable (prevented by construction — the namespace is always derived from
each entry's own artifact.name, which BR-020 already guarantees is unique within the manifest)
| Valid | Invalid |
|---|---|
flight-booking-v1.0.0 and trip-booking-frontend-v1.0.0 as separate tag patterns |
Both entries matching a shared bare v1.0.0 tag |
BR-023: Mono-Repo Root CI Only Runs an Entry's Pipeline When Its Subdirectory Changed¶
Category: Policy
Statement: The mono-repo root's generated CI configuration triggers a given entry's pipeline
only when that entry's own subdirectory changed (GitLab: rules: changes: ['<artifact.name>/**']
gating a child-pipeline trigger job; GitHub: paths: ['<artifact.name>/**'] on that entry's own
workflow's push/pull_request triggers). An entry's release/tag trigger is scoped by its
artifact-prefixed tag pattern (BR-022) instead, not by path, since the release tier never
rebuilds anything (see ADR-006).
Rationale: Avoids running every artifact's full CI pipeline on every push when only one
artifact actually changed — mirrors the per-image rules: changes: gating already used in the
build-images repository.
Applies to: Each item of MonoRepoConfig.Repos, at CI-file generation time
Violated when: An entry's Pre-Check/Build/Test tier pipeline runs on a push or merge request that touched no file under that entry's own subdirectory.
Error code: not applicable
| Valid | Invalid |
|---|---|
Only flight-booking/** changed → only flight-booking's pipeline runs |
flight-booking's pipeline runs on a push that only touched trip-booking-frontend/** |
BR-024: Mono-Repo Generation Scaffolds Root-Level System-Wide Specs¶
Category: Invariant
Statement: Generating from a .mono-repo.yaml manifest additionally writes a root-level
specs/architecture/ADRs/README.md, specs/domain/business_rules.md, and specs/index.md for
content that spans more than one artifact — alongside, not instead of, each entry's own full
specs/ tree in its own subdirectory.
Rationale: Cross-artifact architecture decisions and invariants (e.g. which technology
orchestrates a saga across services) don't belong to any single artifact's own specs/; without
a fixed home they end up undocumented or duplicated into one artifact's specs by habit. See
ADR-013.
Applies to: MonoRepoConfig, at generation time
Violated when: A mono-repo is generated without a root-level specs/architecture/ADRs/,
specs/domain/business_rules.md, or specs/index.md.
Error code: not applicable
| Valid | Invalid |
|---|---|
Root specs/architecture/ADRs/README.md and specs/domain/business_rules.md present after repo init from a manifest |
Root specs/ absent or only present under an entry's own subdirectory |
BR-025: Java Mono-Repo Entries Must Specify a Module Path¶
Category: Invariant
Statement: A .mono-repo.yaml entry with language: java must specify a non-empty
artifact.module.
Rationale: The Java package name is derived from artifact.module (see deriveJavaPackage
in presentation_model.go). Go's toolchain refuses to build without a real go.mod module
path, so an empty value is caught immediately and obviously — but Java compiles fine with none
at all, silently falling back to the placeholder package com.example.app. Left unnoticed, a
whole service ships under a placeholder namespace instead of a real one. Catching this at
generation time, with a clear error naming the offending entry, is cheaper than discovering it
after implementation and having to rename the package, every import, and the Maven groupId
retroactively.
Applies to: Each item of MonoRepoConfig.Repos where language is java
Violated when: entry.language is java (case-insensitive) and entry.artifact.module is
empty or blank.
Error code: not applicable — a formatted error naming the entry's artifact.name and
suggesting a reverse-DNS-style module path
| Valid | Invalid |
|---|---|
language: java, artifact.module: example.org/myteam/my-service |
language: java, artifact.module absent or blank |
language: go, artifact.module absent (Go's own toolchain enforces this separately) |