Skip to content

Capability 002: Modules

Status: Accepted

Context

A developer selects per artifact what repo update generates, in coarse modules rather than file by file (mission statement; target picture, sections Modules and Commands). This capability defines the standard modules, their defaults and dependencies, which files a module owns, how repo update writes them, and repo check. It builds on the configuration of capability 001.

References

Details

Standard modules:

Module Generates Default
scaffold Source and test tree, build files, Makefile, Devbox, lint configuration, README.md, LICENSE, .gitignore on
ci CI pipeline, cliff.toml, CHANGELOG.md on
specs specs/ tree, mkdocs.yml, make docs on
domain-model specs/functional_design/domain_model.md on for service, off otherwise
bdd BDD harness, test/bdd/, make bdd-test on, except for electronics
manual-test test/manual/ with the manual test template on for firmware-stm32 and electronics, off otherwise
ai CLAUDE.md, .claude/, .mcp.json, Copilot files, each for the assistants in assistants on
guidelines Every applicable guideline in guidelines/ at the repository root, listed in the docs site off

Dependencies:

Module Needs
ci scaffold
bdd scaffold
domain-model specs

Ownership of a file:

Owner Written by repo update Examples
Generator On every run .claude/rules/, CI pipeline, Makefile, mkdocs.yml, guidelines
Developer Once, when the configuration first generates it README.md, source code, specs/, Makefile.local, Compose files

The docs site takes in guidelines/ while it is built, so no copy below specs/ is committed. In a mono-repository, the artifacts' guidelines go to guidelines/ at the repository root as well.

meta records the modules generated per artifact:

meta:
  generatorVersion: 2.0.0
  artifacts:
    bridge: [scaffold, ci, specs, bdd, manual-test, ai]

Functional Requirements

  1. Without modules, an artifact gets the default modules of its type. With modules, it gets exactly the listed ones.
  2. A module that is not on, by default or by selection, generates no file, no Makefile target, and no CI job.
  3. repo update writes the generator-owned files of every selected module.
  4. repo update writes a developer-owned file only if it does not exist and the configuration recorded in meta.artifacts would not generate it: on the first run, for a new artifact, for a module added later, and for a repository-level file that a new artifact needs, such as the Compose files with the first service. It never overwrites an existing developer-owned file.
  5. After a successful run, meta.artifacts lists, per artifact, the modules generated.
  6. repo update deletes no file. For a module or artifact recorded in meta.artifacts but no longer configured, it prints each existing file the module or artifact would generate, one path per line on standard output, and removes the record from meta.artifacts.
  7. repo check validates .repo.yaml like repo update, then prints, one path per line, every file that repo update would create or change, .repo.yaml included, and exits non-zero if there is one. It writes nothing.
  8. A repository-level file of a mono-repository exists as soon as one artifact selects the module that needs it: the root pipeline with ci, the combined docs site with specs, the root CLAUDE.md and .claude/ with ai, guidelines/ with guidelines.
  9. Validation (capability 001, Functional Requirement 7) additionally reports:
    1. an unknown module
    2. a selected module whose dependency is not selected, naming the missing module
  10. A generated file refers to no file that only a module not selected generates. Guidelines and the rules taken from them are exempt.
  11. For an existing repository-level developer-owned file whose generated content differs between the recorded and the current configuration, repo update prints review: and its path on standard output.

Non-Functional Requirements

  1. repo check and repo update produce the same list of paths for the same repository state.

Technical Requirements

None.

Error Handling

  1. repo update refuses a Git working tree with changes other than to .repo.yaml, names the changed files, and writes nothing.
  2. repo update and repo check outside a Git working tree fail with a message and write nothing.

Out of Scope

  • The content a module generates, beyond the files named above
  • Interface modules, such as can
  • Removing files the generator no longer generates

Acceptance Criteria

  1. Go service without modules: specs/functional_design/domain_model.md, test/bdd/, and no test/manual/, no guidelines/.
  2. KiCad electronics without modules: test/manual/, no test/bdd/, no make bdd-test.
  3. Go tool with modules: [scaffold, specs]: no CI pipeline, no CLAUDE.md, no test/bdd/, no make bdd-test.
  4. modules: [ci]: reported as "ci needs scaffold"; modules: [scaffld]: reported as unknown.
  5. Generated repository, README.md changed and committed, .claude/rules/core.md changed and committed, repo update: README.md keeps the change, .claude/rules/core.md is regenerated.
  6. Generated repository with modules: [scaffold, specs], bdd added, repo update: test/bdd/ is generated; README.md, changed and committed before, keeps the change.
  7. Generated repository, sample source file deleted and committed, repo update: the file stays deleted.
  8. Generated repository with bdd, bdd removed, repo update: every file below test/bdd/ is printed and still exists; meta.artifacts no longer lists bdd.
  9. Mono-repository, artifact removed from artifacts, repo update: the files of its directory are printed and still exist.
  10. Generated, committed repository, repo check: prints nothing, exits 0.
  11. Same repository, .claude/rules/core.md changed and committed: repo check prints .claude/rules/core.md, exits non-zero, and the file keeps the change.
  12. Same repository, bdd added to .repo.yaml: repo check prints the files of test/bdd/ and .repo.yaml, exits non-zero.
  13. .repo.yaml changed and not committed, repo update: runs. README.md changed and not committed: refused, naming README.md.
  14. Mono-repository with two artifacts, only one with ai: root CLAUDE.md exists.
  15. Every profile with both assistants, each module left out in turn together with the modules that need it: no generated Markdown file other than a guideline or a rule taken from one names in backticks a path that the profile generates with all modules but this repository lacks.
  16. Generated repository, README.md changed and a sample source file deleted, meta.artifacts removed, all committed, repo update: README.md keeps the change; the sample source file is generated again.
  17. Mono-repository with a Go tool only, generated and committed, a Go service added, repo update: docker-compose.yml and docker-compose.e2e.yml exist.
  18. Same repository, committed, a second Go service added, repo update: both Compose files are unchanged, and review: docker-compose.yml and review: docker-compose.e2e.yml are printed.
  19. Mono-repository with a Go service, generated, docker-compose.yml deleted, all committed, a Go tool added, repo update: docker-compose.yml stays deleted.
  20. Every profile except electronics, without modules: test/bdd/features/ holds a feature file, and the Makefile has the target bdd-test.

Decisions

  1. meta.artifacts records the generated modules instead of repo update writing missing developer-owned files. A developer deletes sample files on purpose; regenerating them on the next run would be wrong. Consequence: meta grows with the artifacts, and a developer-owned file deleted by mistake is restored only by removing its module from meta or by Git.
  2. Generator-owned files are overwritten without asking; the clean working tree is the safety net. A change to such a file shows up in git diff after repo update. Consequence: a developer who wants to keep a change to a generator-owned file deselects the module that owns it.
  3. .repo.yaml is exempt from the clean working tree. Editing it and running repo update is the normal workflow; requiring a commit in between would be tedious.
  4. The generator never deletes. Deleting is the developer's decision, and git rm makes it reviewable. repo update lists the candidates.
  5. A module never pulls in its dependency on its own. An implicit addition would surprise the developer as much as a missing file; the violation names what to add.
  6. Guidelines live in guidelines/ at the repository root, not below specs/. They apply to the repository but do not specify it. Consequence: the docs site configuration takes them in by its own means, a gen-files script in a single repository and an included guidelines/mkdocs.yml in a mono-repository.
  7. Guidelines and the rules taken from them name files regardless of the selected modules. A guideline reads the same for every profile (capability 005); it states conventions, such as where feature files go, that hold before the files exist. Consequence: a rule may name a directory that a repository without the module lacks.
  8. repo update never overwrites an existing developer-owned file. A repository that existed before its first repo update, or one whose meta was lost, keeps the developer's content; only missing files are added. Consequence: a developer-owned file that a newer generator version writes differently keeps its old content.
  9. A developer-owned file is new when the configuration recorded in meta.artifacts would not generate it. Recording modules alone cannot tell whether a repository-level file, such as a Compose file, depends on the set of artifacts; at the root, every module counts as recorded after the first run. Consequence: repo update renders the recorded configuration as well.