Schema version: 1
<repo-root>/.modules/<name>/module.toml
.modules/<name>/module.toml is the authoritative record for a module named
<name>. No root index file; discovery is "list .modules/, validate each
subdirectory's module.toml." Each tool manages only its own subdirectory.
schema_version = 1 # required
name = "<module-name>" # required; MUST equal parent directory name
version = "<semver>" # required; display/debug only
description = "<one-line text>" # optional
capabilities = ["<cap>", "..."] # optional
[config] # optional; module-specific; NOT read by dev-modules
# arbitrary keys ignored by the spec| Field | Type | Required | Notes |
|---|---|---|---|
schema_version |
int | yes | Readers MUST refuse unknown values. |
name |
str | yes | [a-z][a-z0-9-]*, matches parent directory name. |
version |
str | yes | Semver or similar; not validated. |
description |
str | no | One line. |
capabilities |
list[str] | no | Namespaced (<module>.<feature>). See below. |
[config] |
table | no | Free-form; consumers may read it but dev-modules doesn't interpret. |
Capability strings are namespaced by module:
<module-name>.<dotted-feature>
Examples:
workshop.journal.readworkshop.grid.intaketelegram.notifytelegram.notify.inline_buttonsccweb.ui.sidebar
A module SHOULD only advertise capabilities under its own namespace. Consumers check capabilities before using a feature:
if has_capability("workshop", "workshop.journal.read"):
...A module <name> is installed iff both:
<repo>/.modules/<name>/exists and is a directory.<repo>/.modules/<name>/module.tomlparses successfully with a compatibleschema_versionand anamefield matching the directory name.
Either condition failing → not installed. Consumers MUST gracefully handle
absence — no exceptions, just a False / empty return.
Readers walk upward from the starting directory (default: cwd) and use the
nearest ancestor containing a .modules/ directory. If none is found,
they return an empty result.
Each tool writes/removes its own manifest during install/uninstall:
- Install: write
.modules/<name>/module.tomlatomically (write to a temp file, then rename). This avoids torn reads if another process is enumerating simultaneously. - Uninstall: remove
.modules/<name>/.
Multiple tools installing concurrently is safe because each touches a different subdirectory.
- No event bus. Consumers call producers' public APIs directly.
- No config store. Module config lives wherever the module normally stores it.
- No runtime code loading. The manifest only gates behavior of already-installed code.
- No version negotiation. Use capabilities (feature flags) for API
contracts;
versionis display-only.
schema_version = 1is the current version.- Future versions MUST add new optional fields only, or bump
schema_versionand require a reader update. - Readers MUST refuse unknown
schema_versionvalues rather than guess — returning "not installed" is the safe default.