valise-sh 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +152 -2
  2. package/README.md +99 -146
  3. package/bin/valise +57 -5376
  4. package/docs/architecture.md +77 -121
  5. package/docs/contrib-modules.md +172 -218
  6. package/docs/installation.md +137 -83
  7. package/docs/module-abi.md +125 -126
  8. package/docs/projects.md +249 -0
  9. package/docs/release-policy.md +68 -93
  10. package/docs/shared-modules.md +40 -39
  11. package/examples/README.md +12 -11
  12. package/examples/e2e/README.md +59 -66
  13. package/examples/e2e/bundles.tsv +6 -0
  14. package/examples/e2e/module.d/e2e_aliases.meta +1 -2
  15. package/examples/e2e/module.d/e2e_completions_bash.meta +0 -1
  16. package/examples/e2e/module.d/e2e_completions_zsh.meta +0 -1
  17. package/examples/e2e/module.d/e2e_env_setup.meta +2 -3
  18. package/examples/e2e/module.d/e2e_fish_features.meta +0 -1
  19. package/examples/e2e/module.d/e2e_generated_config.meta +1 -2
  20. package/examples/e2e/module.d/e2e_ksh_features.meta +0 -1
  21. package/examples/e2e/module.d/e2e_layer_probe.meta +1 -2
  22. package/examples/e2e/module.d/e2e_optional_network.meta +1 -2
  23. package/examples/e2e/module.d/e2e_platform_tools.meta +8 -0
  24. package/examples/e2e/module.d/e2e_platform_tools_bsd.meta +2 -3
  25. package/examples/e2e/module.d/e2e_platform_tools_darwin.meta +2 -3
  26. package/examples/e2e/module.d/e2e_platform_tools_linux.meta +2 -3
  27. package/examples/e2e/valise.conf +4 -0
  28. package/examples/e2e/variants.tsv +3 -0
  29. package/examples/local/module.d/hello_valise.meta +1 -2
  30. package/examples/profile-project/README.md +78 -108
  31. package/examples/profile-project/bundles.tsv +8 -0
  32. package/examples/profile-project/{shell/layers → layers}/demo/module.d/contrib_demo_greeting.meta +1 -2
  33. package/examples/profile-project/{shell/module.d → module.d}/greeting.meta +1 -2
  34. package/examples/profile-project/{shell/module.d → module.d}/profile_main.meta +0 -1
  35. package/examples/profile-project/{shell/modules → modules}/greeting.sh +1 -1
  36. package/examples/profile-project/valise.conf +4 -0
  37. package/examples/profile-project/variants.tsv +3 -0
  38. package/examples/shared/README.md +13 -34
  39. package/examples/shared/module.d/contrib_shared_paths.meta +2 -3
  40. package/examples/shared/module.d/contrib_shared_umask.meta +1 -2
  41. package/examples/shared/modules/shared/contrib_shared_paths.sh +2 -2
  42. package/lib/capabilities.sh +40 -13
  43. package/lib/cli/commands.sh +278 -0
  44. package/lib/cli/common.sh +326 -0
  45. package/lib/cli/devtools.sh +539 -0
  46. package/lib/cli/fish-hook.sh +986 -0
  47. package/lib/cli/integration.sh +337 -0
  48. package/lib/cli/lifecycle.sh +578 -0
  49. package/lib/cli/project.sh +1807 -0
  50. package/lib/cli/store.sh +1023 -0
  51. package/lib/cli/usage.sh +308 -0
  52. package/lib/compiler.awk +11 -4
  53. package/lib/fish/loader.fish +69 -9
  54. package/lib/fish/metadata.fish +49 -5
  55. package/lib/fish/profile.fish +170 -0
  56. package/lib/loader.sh +6 -8
  57. package/lib/metadata.sh +111 -5
  58. package/manifests/parser-support.tsv +2 -2
  59. package/module.d/valise_editor.meta +9 -0
  60. package/module.d/valise_history.meta +9 -0
  61. package/module.d/valise_pager.meta +9 -0
  62. package/module.d/valise_path.meta +9 -0
  63. package/module.d/{platform_bsd.meta → valise_platform_bsd.meta} +3 -4
  64. package/module.d/{platform_linux.meta → valise_platform_linux.meta} +3 -4
  65. package/module.d/{platform_macos.meta → valise_platform_macos.meta} +3 -4
  66. package/module.d/{platform_posix.meta → valise_platform_posix.meta} +2 -3
  67. package/module.d/valise_xdg.meta +9 -0
  68. package/modules/system/{contrib_editor.sh → valise_editor.sh} +2 -2
  69. package/modules/system/{contrib_history.sh → valise_history.sh} +2 -2
  70. package/modules/system/{contrib_pager.sh → valise_pager.sh} +2 -2
  71. package/modules/system/{contrib_path.sh → valise_path.sh} +3 -3
  72. package/modules/system/{contrib_xdg.sh → valise_xdg.sh} +2 -2
  73. package/package.json +91 -10
  74. package/docs/variants.md +0 -327
  75. package/entrypoints/.gitkeep +0 -0
  76. package/entrypoints/fish/profile.fish +0 -99
  77. package/examples/e2e/build-manifest.sh +0 -138
  78. package/examples/local/.gitkeep +0 -0
  79. package/examples/profile-project/shell/release/bundle-roots +0 -9
  80. package/examples/profile-project/shell/release/install-variants +0 -3
  81. package/examples/profile-project/shell/release/module-registry.tsv +0 -4
  82. package/examples/profile-project/shell/release/project.conf +0 -3
  83. package/lib/.gitkeep +0 -0
  84. package/manifests/.gitkeep +0 -0
  85. package/module.d/.gitkeep +0 -0
  86. package/module.d/contrib_editor.meta +0 -10
  87. package/module.d/contrib_history.meta +0 -10
  88. package/module.d/contrib_pager.meta +0 -10
  89. package/module.d/contrib_path.meta +0 -10
  90. package/module.d/contrib_xdg.meta +0 -10
  91. package/modules/.gitkeep +0 -0
  92. /package/examples/profile-project/{shell/layers → layers}/demo/modules/shared/contrib_demo_greeting.sh +0 -0
@@ -1,246 +1,200 @@
1
- # Contributing Modules
1
+ # Writing modules
2
2
 
3
- This guide covers modules loaded by the valise-sh engine. A module is
4
- usually a metadata file and a shell body; consumer projects may add their
5
- own naming, review, and release rules.
3
+ A module is a metadata file and, usually, a shell body. This guide covers
4
+ writing one in a valise project, checking and testing it, and deciding
5
+ whether it belongs in a shareable layer. [module-abi.md](module-abi.md) is
6
+ the full metadata contract; [projects.md](projects.md) covers bundles,
7
+ variants, and builds.
6
8
 
7
- ## Module structure
8
-
9
- A file-backed module has a metadata file and a body. POSIX bodies usually
10
- end in `.sh`; Fish bodies end in `.fish`:
9
+ ## Create a module
11
10
 
11
+ ```sh
12
+ valise new demo
13
+ cd demo
14
+ valise module new hello --exports=hello
12
15
  ```
13
- module.d/<name>.meta
14
- modules/<area>/<name>.sh
16
+
17
+ `valise module new` writes `module.d/hello.meta` and `modules/hello.sh` and
18
+ adds `hello` to the `requires=` of the bundle's POSIX root, so a build
19
+ loads it. When the default bundle has several POSIX roots, name the ones to
20
+ change with `--into=<module>[,<module>...]`, or pass `--no-wire` to wire
21
+ the module yourself. Edit the body, then:
22
+
23
+ ```sh
24
+ valise lint # check metadata, bundles, variants, and syntax
25
+ valise apply # build, install, write the integration, verify
15
26
  ```
16
27
 
17
- The metadata file is read by the engine before any body is sourced. It uses
18
- one `key=value` field per line:
28
+ A module's metadata looks like this:
19
29
 
20
30
  ```text
21
- name=example_tool
22
- requires=
31
+ name=hello
32
+ version=1.0.0
33
+ side_effect=read-only
34
+ requires=valise_path
23
35
  optional=
24
- shells=sh bash zsh ksh
36
+ shells=posix
25
37
  platforms=any
26
- side_effect=read-only
27
- exports=example_tool_loaded
28
- updates=
29
- version=1.0.0
30
- load=modules/system/example_tool.sh
38
+ exports=hello
39
+ commands=hello
40
+ tests=tests/hello.sh
41
+ load=modules/hello.sh
31
42
  ```
32
43
 
33
- ### Metadata fields
34
-
35
- | Field | Required | Description |
36
- |---|---|---|
37
- | `name` | Yes | Stable identifier matching `[a-z_][A-Za-z0-9_]*` |
38
- | `requires` | No | Space-separated hard dependencies (loaded first) |
39
- | `optional` | No | Space-separated best-effort dependencies |
40
- | `shells` | Yes | Supported shells: `sh`, `bash`, `zsh`, `ksh`, `fish`. `sh` means every POSIX shell (sh, bash, zsh, ksh, dash) |
41
- | `platforms` | Yes | `any`, `darwin`, `linux`, or BSD family values |
42
- | `side_effect` | Yes | One of: `read-only`, `path-env`, `generated-file`, `update-network`, `install-time`, `local-secret`, `local-state`, `work-only` |
43
- | `exports` | No | Public functions this module declares |
44
- | `updates` | No | Update registration identifiers mapped in the project update manifest |
45
- | `version` | No | Module version (digit-first, digits/letters/`.`/`_`/`-`); different versions of one name across roots fail closed with a skew diagnostic |
46
- | `load` | Conditionally | Repository-relative path to the module body |
47
-
48
- At least one of `load` or `requires` must be present. `load` and `requires`
49
- are not mutually exclusive — a module
50
- may declare both and will load its dependencies first, then its own body. A
51
- module with `requires` and no load target is a **meta module**: it names
52
- dependencies and has no body. See
53
- [module-abi.md](module-abi.md#meta-modules).
54
-
55
- ### Naming
56
-
57
- Modules that live only in a layer root (see
58
- [module-abi.md](module-abi.md) multi-root search path) must carry the
59
- `contrib_` name prefix; the loader rejects unprefixed layer-only modules
60
- fail closed. Modules in the primary root have no prefix requirement.
61
- Same-name modules across roots must be byte-identical or the load fails
62
- closed naming both paths.
63
-
64
- ## Engine capabilities
65
-
66
- Use `sp_cap_*` functions from `lib/capabilities.sh` instead of open-coding
67
- platform-specific commands. The POSIX entrypoint sources the capability
68
- library before the loader, so module bodies can call these directly.
69
-
70
- **The helpers stay available after the profile loads.** A compiled plan
71
- sources the capability prelude and leaves it in place, so a function your
72
- module defines can call `sp_cap_os` or any other helper long after the plan
73
- finished loading. Both the compiled plan and the loader keep the helpers
74
- available at runtime.
75
-
76
- Available capabilities include:
77
-
78
- - `sp_cap_canonical_path <path>` — resolve absolute path through symlinks
79
- - `sp_cap_path_under <base> <target>` — check if target is under base
80
- - `sp_cap_file_mtime <path>` — portable modification time (epoch seconds)
81
- - `sp_cap_mktemp_dir [prefix] [parent]` — secure temporary directory
82
- - `sp_cap_mktemp_file <parent> <prefix>` — secure temporary file
83
- - `sp_cap_homebrew_prefix` — locate Homebrew installation
84
- - `sp_cap_command_exists <name>` — portable command existence check
85
- - `sp_cap_command_path <name>` — resolve command to absolute path
86
- - `sp_cap_os` — print normalized OS name
87
- - `sp_cap_cpu_count` — print available CPU count
88
-
89
- See [module-abi.md](module-abi.md) for the full capability contract and
90
- [architecture.md](architecture.md) for scope. Shared modules — the ones
91
- other people consume — carry additional governance: namespace ownership,
92
- review, deprecation, and removal are policy questions, and the written
93
- policy plus the curated index lives in
94
- [shared-modules.md](shared-modules.md).
95
-
96
- ## Authoring workflow
97
-
98
- ### In a consuming project
99
-
100
- Inside a project that uses the CLI, the CLI creates the files for you:
44
+ Choose `shells` by what the body needs:
45
+
46
+ - `posix` for a body that runs in every POSIX shell: dash, BusyBox ash,
47
+ bash, zsh, and ksh.
48
+ - `sh` for a body that runs only in the generic class (dash, ash, and other
49
+ plain `sh` implementations), listed together with the explicit shells it
50
+ also supports: `shells=sh bash ksh` loads in dash, bash, and ksh, and not
51
+ in zsh.
52
+ - `bash`, `zsh`, or `ksh` for a body that uses that shell's features.
53
+ - `fish` for a native Fish body (`.fish`). A Fish module never runs a POSIX
54
+ body, so a feature that needs both shells is two modules.
55
+
56
+ Zsh runs module functions with the user's options. A body that relies on
57
+ word splitting of unquoted expansions behaves differently in zsh, where it
58
+ is off by default; quote expansions, split explicitly, or leave zsh out of
59
+ `shells`.
60
+
61
+ `side_effect` says what the body does when sourced. Startup modules use
62
+ `read-only`, `path-env`, `local-state` (writes under the XDG state
63
+ directory), `local-secret` (reads secrets that live on this machine), or
64
+ `work-only`. A build refuses `generated-file`, `update-network`, and
65
+ `install-time` in a startup closure.
66
+
67
+ List every public function in `exports`. The plan refuses to load a module
68
+ whose export already exists, checks that each export is defined after the
69
+ body runs, and removes them again when the module fails. List exports that
70
+ should also run from scripts and other programs in `commands`; see
71
+ [module-abi.md](module-abi.md#published-commands).
72
+
73
+ Use the `sp_cap_*` helpers from the capability prelude instead of
74
+ platform-specific commands. They stay defined after the profile loads, so
75
+ your functions can call them later:
76
+
77
+ - `sp_cap_os`, `sp_cap_cpu_count`, `sp_cap_homebrew_prefix`
78
+ - `sp_cap_command_exists <name>`, `sp_cap_command_path <name>`
79
+ - `sp_cap_canonical_path <path>`, `sp_cap_path_under <base> <target>`
80
+ - `sp_cap_file_mtime <path>`
81
+ - `sp_cap_secure_local_bin_check <home> [reject|allow-owned-symlinks]`
82
+ - `sp_cap_mktemp_dir [prefix] [parent]`, `sp_cap_mktemp_file <parent> <prefix>`
83
+
84
+ `sp_cap_secure_local_bin_check` defaults to `reject`, which refuses every
85
+ symlink in `~/.local/bin`. `allow-owned-symlinks` accepts a symlink whose
86
+ link entry is owned by the current user (the pipx and uv shape) while still
87
+ rejecting unowned entries and group- or other-writable regular files.
88
+
89
+ The engine also ships modules a project can require: `valise_path`
90
+ (`valise_path_prepend` and `valise_path_append`), `valise_editor`,
91
+ `valise_history`, `valise_pager`, `valise_xdg`, and the `valise_platform_*`
92
+ modules. [shared-modules.md](shared-modules.md) lists them.
93
+
94
+ ## Checking and testing modules
95
+
96
+ `valise lint [--require-parsers] [module...]` checks the project and reports
97
+ every problem it finds:
98
+
99
+ - each metadata record, its name, its prefix, and the files it names;
100
+ - each layer: `contrib_` names, `version=`, safe file modes, and
101
+ dependencies that stay inside the layer or the engine;
102
+ - the `lint_layer_forbid` pattern from `valise.conf`, if set;
103
+ - every bundle compiled for every variant and shell, which catches unknown
104
+ roots and exclusions, missing dependencies, and exclusions that break a
105
+ remaining module;
106
+ - the syntax of each body and test, with the parser of every shell the module
107
+ declares (`dash` or a plain `sh`, `bash -n`, `zsh -n`, `ksh -n`, and
108
+ `fish --no-execute`). A missing parser is reported as a skip, or as a
109
+ failure with `--require-parsers`.
110
+
111
+ `valise test [--shell=<class>] [--require-shells] [module...]` runs each
112
+ module's `tests=` scripts. For every shell class the module declares whose
113
+ interpreter is installed, it builds a release with the module as its only
114
+ root, starts the shell with `env -i` and a fresh `HOME`, `TMPDIR`, and XDG
115
+ tree, loads the module in strict mode, and runs the test. `PATH` is
116
+ `${VALISE_TEST_PATH:-/usr/bin:/bin:/usr/sbin:/sbin}` plus the interpreter's
117
+ directory. A test passes when it exits 0:
101
118
 
102
119
  ```sh
103
- valise new demo # scaffold a project
104
- cd demo
105
- valise module new hello --exports=hello
120
+ # tests/hello.sh
121
+ [ "$(hello world)" = 'hello world' ] || { echo "got: $(hello world)"; exit 1; }
106
122
  ```
107
123
 
108
- `valise module new` writes `shell/module.d/hello.meta` and
109
- `shell/modules/hello.sh`, and wires `hello` into the seed aggregator's
110
- `requires=` so the module is actually reachable. It refuses a duplicate
111
- export name. Edit the body, then:
124
+ The output names each run (`ok hello [bash] tests/hello.sh`), shows a
125
+ failing test's output, and ends with a count; any failure makes the command
126
+ fail. A test that needs a file before the module loads (a secret, a config
127
+ file) writes it and calls `valise_test_reload`, which loads the module again.
128
+ A test that starts another shell loads the module there from
129
+ `VALISE_TEST_PLAN`, the plan the test was loaded with, with
130
+ `VALISE_MODULE_ROOT` set to `VALISE_TEST_RELEASE`:
112
131
 
113
132
  ```sh
114
- valise build # build to build/release
115
- valise apply # build, install, integrate, verify
133
+ bash -c 'VALISE_MODULE_ROOT=$VALISE_TEST_RELEASE; . "$VALISE_TEST_PLAN"; hello world'
116
134
  ```
117
135
 
118
- You never hand-write the registry: `valise build` and `valise apply`
119
- regenerate `shell/release/module-registry.tsv` from the metadata.
120
-
121
- ### In the engine repository
122
-
123
- 1. Write the two files. Start from an existing pair — `module.d/contrib_pager.meta`
124
- plus `modules/system/contrib_pager.sh` is the smallest real example.
125
- 2. Validate the metadata as data:
126
-
127
- ```sh
128
- . ./lib/metadata.sh
129
- sp_metadata_read module.d/example_tool.meta
130
- ```
131
-
132
- The reader prints the normalized record or a precise diagnostic
133
- (`invalid side_effect`, `unknown metadata field`, and so on). It never
134
- sources the body.
135
- 3. Load it through the entrypoint and call the export:
136
-
137
- ```sh
138
- VALISE_MODULE_ROOT="$PWD" \
139
- VALISE_MODULES='example_tool' \
140
- VALISE_MODE=strict \
141
- . ./entrypoints/profile.sh
142
- example_tool_loaded
143
- ```
144
-
145
- Strict mode fails closed on any problem: missing dependency, a declared
146
- export missing after the body runs, an export that already exists,
147
- reserved-utility shadowing, shell/platform mismatch. The loader checks
148
- what `exports=` declares; it does not reject a body that defines extra
149
- private functions, so list everything you consider public. Under zsh
150
- the entrypoint handles word-splitting itself; source it the same way.
151
- 4. Check parser coverage. `manifests/parser-support.tsv` declares which
152
- POSIX shells must parse each file class. Place portable bodies under a
153
- path pattern the table already covers (for example `modules/system/*.sh`)
154
- or add a row for your pattern, then run the gate:
155
-
156
- ```sh
157
- make test-parser-support
158
- ```
159
-
160
- This target belongs to the engine's source repository. The npm package
161
- ships the runtime and the docs, not the Makefile or the parser table, so
162
- a package consumer checks parser coverage by hand: run every POSIX shell
163
- you claim to support over the body (`sh -n`, `bash -n`, `zsh -n`,
164
- `ksh -n`) and confirm each one accepts it.
165
-
166
- 5. Publish a release containing your module tree and prove the whole
167
- path end to end (make-manifest, install, release verify):
168
-
169
- ```sh
170
- valise make-manifest ./mytree
171
- cd mytree && valise install && valise release verify
172
- ```
173
-
174
- See [installation.md](installation.md) for release integrity and
175
- provenance.
176
-
177
- ## Shipped modules
178
-
179
- The reusable `contrib_` modules are listed in
180
- [shared-modules.md](shared-modules.md). For example, load PATH and editor
181
- defaults from a checkout with:
136
+ Tests see `VALISE_TEST_ROOT` (the scratch directory, which holds `HOME`),
137
+ `VALISE_TEST_MODULE`, `VALISE_TEST_MODULE_DIR`, `VALISE_TEST_SHELL`,
138
+ `VALISE_TEST_RELEASE`, `VALISE_TEST_PLAN` (`runtime/fish/profile.fish` in
139
+ Fish), and `VALISE_PROJECT_ROOT`. Fish tests (`.fish`) run only in Fish; other tests
140
+ run only in POSIX shells.
182
141
 
183
- ```sh
184
- VALISE_MODULE_ROOT="$PWD" \
185
- VALISE_MODULES='contrib_path contrib_editor' \
186
- VALISE_MODE=strict \
187
- . ./entrypoints/profile.sh
188
- ```
189
- ## Testing checklist
190
-
191
- For every new module, run each step and confirm the named behavior. The
192
- commands in steps 1 to 3 work from a checkout of this repository or from an
193
- npm-installed package; the `make` targets belong to the source repository
194
- only.
195
-
196
- 1. `sp_metadata_read module.d/<name>.meta` prints the normalized record
197
- (step 2 above) and the load path is covered by a
198
- `manifests/parser-support.tsv` row (`make test-parser-support` in the
199
- source repository).
200
- 2. Source the module in a clean environment — the body prints nothing when
201
- its prerequisites are absent and defines every name in `exports=`:
202
-
203
- ```sh
204
- env -i HOME="$PWD/.fresh-home" PATH=/usr/bin:/bin \
205
- VALISE_MODULE_ROOT="$PWD" VALISE_MODULES='<name>' VALISE_MODE=strict \
206
- /bin/sh -c '. ./entrypoints/profile.sh'
207
- ```
208
-
209
- (Create `./.fresh-home` first or point `HOME` at any empty directory;
210
- never source against your live profile.)
211
- 3. Source it twice in one strict session or call the entrypoint repeatedly:
212
- the second load must fail closed with `declared export already exists`
213
- (the loader forbids double-definition), and calling an idempotent
214
- function twice must leave the same state.
215
- 4. The body performs no writes at source time: `side_effect` is
216
- `read-only`, `path-env`, or `local-state`, and `generated-file`/
217
- `update-network`/`install-time` bodies are rejected during ordinary
218
- source.
219
- 5. Every public function appears in `exports=` — the loader verifies each
220
- declared export exists after the body runs, and cleans them up on
221
- failure.
222
- 6. Tests run in quarantined homes: this repository's suites already use
223
- fresh temporary `HOME`, controlled `TMPDIR` and `PATH`, and cleared
224
- startup hooks (`make check-fast` to confirm the engine gate). Stub
225
- network and secrets in your own tests; never read a live profile.
226
-
227
- Run the engine's own fast gate while authoring:
142
+ `valise run [--shell=<class>] [--module=<m>]... [--bundle=<b>]
143
+ [--variant=<v>] [--keep-env] -- <script> [args...]` loads modules into a
144
+ shell and runs a script there, for quick checks:
228
145
 
229
146
  ```sh
230
- make check-fast
147
+ valise run --shell=zsh --module=hello -- 'hello "$@"' a b
148
+ valise run --bundle=work -- 'command -v my_work_function'
231
149
  ```
232
150
 
233
- and the full matrix (`make VALISE_REQUIRE_TOOLING=1 check`) before claiming
234
- release readiness. The extended-runtime lanes are strict in CI and can be
235
- required locally with the same override. Both targets live in the source
236
- repository; the npm package ships the runtime and docs, not the Makefile.
237
-
238
- `valise layer-lint <layer-name>` validates a contribution layer's
239
- structure, namespace compliance, file permissions, metadata integrity, and
240
- dependency closure. It needs no Python, but it does need a layer tree: a
241
- provider directory under a consuming project's `shell/layers/`, or a path to
242
- a layer tree containing `module.d/`. In a consuming project:
151
+ Its environment is quarantined like `valise test` unless `--keep-env` is
152
+ given, and its exit status is the script's.
153
+
154
+ None of these commands installs anything or reads your startup files. To
155
+ check a layer tree outside a project, run `valise layer-lint <path>`.
156
+
157
+ ## What belongs in a layer
158
+
159
+ A layer under `layers/<provider>/` is a set of modules another person could
160
+ vendor unchanged. Before moving a module into a layer, check:
161
+
162
+ - **No employer material**: no account IDs, hostnames, cloud profiles,
163
+ internal project names, or anything else tied to one organization.
164
+ - **No personal configuration**: no home-directory paths, chosen editor,
165
+ toolchain, model, or theme.
166
+ - **No dependency outside the layer** other than engine modules. A
167
+ dependency on one of your primary modules cannot be satisfied in someone
168
+ else's project.
169
+ - **Mechanism, not preset.** Keep a reusable engine (a git helper, a path
170
+ function) separate from the presets that configure it for one person. Ship
171
+ the engine in the layer and keep presets in the primary modules, or in a
172
+ separate layer module that requires the engine.
173
+ - **One purpose per module.** A grab-bag module forces a consumer to take
174
+ everything or nothing.
175
+ - **An owner segment**: name modules `contrib_<owner>_<thing>`, so two
176
+ providers do not collide on a name.
177
+ - **A version.** Every layer module needs `version=`; bump it on an
178
+ incompatible change.
179
+
180
+ Set `lint_layer_forbid` in `valise.conf` to a pattern for the identifiers
181
+ that must never reach the layer, such as an employer name, and `valise lint`
182
+ will fail when one appears. [shared-modules.md](shared-modules.md) covers
183
+ ownership, review, and deprecation for layers other people consume.
184
+
185
+ ## Modules in the engine repository
186
+
187
+ The engine's own modules live in this repository's `module.d/` and
188
+ `modules/` and use the `valise_` prefix. `manifests/parser-support.tsv`
189
+ declares which parsers must accept each file class; it ships in the package,
190
+ and `make test-parser-support` checks it in the source repository. Load an
191
+ engine module from a checkout without installing anything through the
192
+ development entrypoint:
243
193
 
244
194
  ```sh
245
- valise layer-lint <provider-name>
195
+ env -i HOME="$(mktemp -d)" PATH=/usr/bin:/bin /bin/sh -c '
196
+ VALISE_MODULE_ROOT="$PWD" VALISE_MODULES="valise_path" VALISE_MODE=strict
197
+ . ./entrypoints/profile.sh && command -v valise_path_prepend'
246
198
  ```
199
+
200
+ Run `make check-fast` while working and `make check` before committing.