container-superposition 0.1.7 → 0.1.8

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 (122) hide show
  1. package/README.md +24 -15
  2. package/dist/scripts/init.js +1 -1537
  3. package/dist/scripts/init.js.map +1 -1
  4. package/dist/tool/cli/args.d.ts +20 -0
  5. package/dist/tool/cli/args.d.ts.map +1 -0
  6. package/dist/tool/cli/args.js +325 -0
  7. package/dist/tool/cli/args.js.map +1 -0
  8. package/dist/tool/cli/run.d.ts +2 -0
  9. package/dist/tool/cli/run.d.ts.map +1 -0
  10. package/dist/tool/cli/run.js +318 -0
  11. package/dist/tool/cli/run.js.map +1 -0
  12. package/dist/tool/commands/doctor.d.ts.map +1 -1
  13. package/dist/tool/commands/doctor.js +141 -6
  14. package/dist/tool/commands/doctor.js.map +1 -1
  15. package/dist/tool/commands/explain.d.ts.map +1 -1
  16. package/dist/tool/commands/explain.js +9 -0
  17. package/dist/tool/commands/explain.js.map +1 -1
  18. package/dist/tool/commands/migrate.d.ts +7 -0
  19. package/dist/tool/commands/migrate.d.ts.map +1 -0
  20. package/dist/tool/commands/migrate.js +52 -0
  21. package/dist/tool/commands/migrate.js.map +1 -0
  22. package/dist/tool/questionnaire/answers.d.ts +16 -0
  23. package/dist/tool/questionnaire/answers.d.ts.map +1 -0
  24. package/dist/tool/questionnaire/answers.js +102 -0
  25. package/dist/tool/questionnaire/answers.js.map +1 -0
  26. package/dist/tool/questionnaire/composer.d.ts +3 -3
  27. package/dist/tool/questionnaire/composer.d.ts.map +1 -1
  28. package/dist/tool/questionnaire/composer.js +691 -27
  29. package/dist/tool/questionnaire/composer.js.map +1 -1
  30. package/dist/tool/questionnaire/presets.d.ts +60 -0
  31. package/dist/tool/questionnaire/presets.d.ts.map +1 -0
  32. package/dist/tool/questionnaire/presets.js +164 -0
  33. package/dist/tool/questionnaire/presets.js.map +1 -0
  34. package/dist/tool/questionnaire/questionnaire.d.ts +10 -0
  35. package/dist/tool/questionnaire/questionnaire.d.ts.map +1 -0
  36. package/dist/tool/questionnaire/questionnaire.js +580 -0
  37. package/dist/tool/questionnaire/questionnaire.js.map +1 -0
  38. package/dist/tool/schema/manifest-migrations.d.ts +5 -0
  39. package/dist/tool/schema/manifest-migrations.d.ts.map +1 -1
  40. package/dist/tool/schema/manifest-migrations.js +45 -0
  41. package/dist/tool/schema/manifest-migrations.js.map +1 -1
  42. package/dist/tool/schema/overlay-loader.d.ts.map +1 -1
  43. package/dist/tool/schema/overlay-loader.js +24 -0
  44. package/dist/tool/schema/overlay-loader.js.map +1 -1
  45. package/dist/tool/schema/project-config.d.ts +13 -1
  46. package/dist/tool/schema/project-config.d.ts.map +1 -1
  47. package/dist/tool/schema/project-config.js +183 -9
  48. package/dist/tool/schema/project-config.js.map +1 -1
  49. package/dist/tool/schema/target-rules.d.ts +78 -0
  50. package/dist/tool/schema/target-rules.d.ts.map +1 -0
  51. package/dist/tool/schema/target-rules.js +367 -0
  52. package/dist/tool/schema/target-rules.js.map +1 -0
  53. package/dist/tool/schema/types.d.ts +38 -1
  54. package/dist/tool/schema/types.d.ts.map +1 -1
  55. package/dist/tool/utils/parameters.d.ts +76 -0
  56. package/dist/tool/utils/parameters.d.ts.map +1 -0
  57. package/dist/tool/utils/parameters.js +125 -0
  58. package/dist/tool/utils/parameters.js.map +1 -0
  59. package/dist/tool/utils/paths.d.ts +2 -0
  60. package/dist/tool/utils/paths.d.ts.map +1 -0
  61. package/dist/tool/utils/paths.js +31 -0
  62. package/dist/tool/utils/paths.js.map +1 -0
  63. package/docs/deployment-targets.md +88 -56
  64. package/docs/examples.md +20 -17
  65. package/docs/filesystem-contract.md +5 -0
  66. package/docs/minimal-and-editor.md +65 -5
  67. package/docs/overlay-imports.md +92 -14
  68. package/docs/overlays.md +113 -28
  69. package/docs/specs/007-init-project-file/spec.md +66 -0
  70. package/docs/specs/007-target-aware-generation/spec.md +126 -0
  71. package/docs/specs/008-project-file-canonical/spec.md +83 -0
  72. package/docs/specs/009-project-env/spec.md +147 -0
  73. package/docs/specs/010-compose-env-materialization/spec.md +130 -0
  74. package/docs/specs/011-overlay-parameters/spec.md +235 -0
  75. package/overlays/.shared/README.md +27 -2
  76. package/overlays/.shared/compose/nvidia-gpu-devcontainer.yml +22 -0
  77. package/overlays/comfyui/.env.example +34 -0
  78. package/overlays/comfyui/README.md +342 -0
  79. package/overlays/comfyui/devcontainer.patch.json +15 -0
  80. package/overlays/comfyui/docker-compose.yml +39 -0
  81. package/overlays/comfyui/overlay.yml +20 -0
  82. package/overlays/comfyui/setup.sh +36 -0
  83. package/overlays/comfyui/verify.sh +103 -0
  84. package/overlays/k3d/README.md +201 -0
  85. package/overlays/k3d/devcontainer.patch.json +9 -0
  86. package/overlays/k3d/overlay.yml +19 -0
  87. package/overlays/k3d/setup.sh +34 -0
  88. package/overlays/k3d/verify.sh +38 -0
  89. package/overlays/ollama/.env.example +14 -0
  90. package/overlays/ollama/README.md +325 -0
  91. package/overlays/ollama/devcontainer.patch.json +14 -0
  92. package/overlays/ollama/docker-compose.yml +24 -0
  93. package/overlays/ollama/overlay.yml +22 -0
  94. package/overlays/ollama/setup.sh +106 -0
  95. package/overlays/ollama/verify.sh +99 -0
  96. package/overlays/open-webui/.env.example +5 -0
  97. package/overlays/open-webui/README.md +162 -0
  98. package/overlays/open-webui/devcontainer.patch.json +14 -0
  99. package/overlays/open-webui/docker-compose.yml +23 -0
  100. package/overlays/open-webui/overlay.yml +38 -0
  101. package/overlays/pgvector/.env.example +6 -0
  102. package/overlays/pgvector/README.md +215 -0
  103. package/overlays/pgvector/devcontainer.patch.json +23 -0
  104. package/overlays/pgvector/docker-compose.yml +32 -0
  105. package/overlays/pgvector/overlay.yml +44 -0
  106. package/overlays/postgres/.env.example +5 -5
  107. package/overlays/postgres/devcontainer.patch.json +4 -4
  108. package/overlays/postgres/docker-compose.yml +10 -6
  109. package/overlays/postgres/overlay.yml +19 -1
  110. package/overlays/qdrant/.env.example +4 -0
  111. package/overlays/qdrant/README.md +216 -0
  112. package/overlays/qdrant/devcontainer.patch.json +20 -0
  113. package/overlays/qdrant/docker-compose.yml +25 -0
  114. package/overlays/qdrant/overlay.yml +40 -0
  115. package/overlays/skaffold/README.md +256 -0
  116. package/overlays/skaffold/devcontainer.patch.json +9 -0
  117. package/overlays/skaffold/overlay.yml +20 -0
  118. package/overlays/skaffold/setup.sh +33 -0
  119. package/overlays/skaffold/verify.sh +24 -0
  120. package/package.json +3 -2
  121. package/tool/schema/config.schema.json +31 -1
  122. package/tool/schema/overlay-manifest.schema.json +33 -0
@@ -0,0 +1,83 @@
1
+ ---
2
+ Feature Branch: copilot/make-superposition-yml-canonical-input
3
+ Created: 2026-03-26
4
+ Status: Implementing
5
+ Input: GitHub issue #134
6
+ ---
7
+
8
+ # Spec: Make superposition.yml the Canonical Input
9
+
10
+ ## Overview
11
+
12
+ Make `superposition.yml` (the project config file) the required, canonical input for all
13
+ generation and regeneration flows. `superposition.json` (the manifest) becomes an output-only
14
+ artifact — a generated receipt, not an input source.
15
+
16
+ ## Motivation
17
+
18
+ Three entry points currently disagree on which file is authoritative, creating user confusion
19
+ and dead code paths. The newest code path (`generate`) already writes `superposition.yml`
20
+ first. This spec promotes that pattern to all entry points.
21
+
22
+ ## Behavioral Changes
23
+
24
+ ### `init` — Always writes `superposition.yml`
25
+
26
+ - Remove `--project-file` flag; writing the project config is now the default.
27
+ - Add `--no-scaffold` flag to skip generating `.devcontainer/` (for project-file-only runs).
28
+ - Scaffold (`.devcontainer/` generation) remains the default for backward compatibility.
29
+ - The project file is written before scaffolding; a scaffold failure does not lose the config.
30
+
31
+ ### `regen` — Reads `superposition.yml` only
32
+
33
+ - Default input: the project config file auto-discovered in the repository root.
34
+ - `--from-manifest <path>` is retained as a **deprecated hidden flag** that emits a warning
35
+ pointing toward `cs migrate`. Existing CI scripts that rely on it will still work but
36
+ receive a deprecation notice.
37
+ - If no project file exists and `superposition.json` is present: error with actionable
38
+ migration guidance: `Run 'cs migrate' to create a project file from your existing manifest.`
39
+ - If neither file exists: error with creation guidance.
40
+
41
+ ### `superposition.json` — Output only
42
+
43
+ - Still written by `composeDevContainer` / `generateManifestOnly` as before.
44
+ - No longer read as a generation input in the standard flow.
45
+ - Still read by `doctor` for diagnostics and drift detection.
46
+
47
+ ## New Commands
48
+
49
+ ### `cs migrate`
50
+
51
+ One-time migration from manifest-only repositories:
52
+
53
+ 1. Discovers or accepts `--from-manifest <path>` to locate `superposition.json`.
54
+ 2. Loads and validates the manifest (with auto-migration for old versions).
55
+ 3. Converts manifest to `ProjectConfigSelection` via the existing
56
+ `buildAnswersFromManifest → mergeAnswers → buildProjectConfigSelectionFromAnswers` pipeline.
57
+ 4. Discovers the output path for the project file (uses existing file if present, otherwise
58
+ defaults to `.superposition.yml`); `--force` to overwrite.
59
+ 5. Writes the project config YAML.
60
+ 6. Prints a success message with next-step guidance (`regen` to regenerate).
61
+
62
+ ## `doctor` Drift Detection
63
+
64
+ Once the project file is canonical, `doctor` can compare the two files:
65
+
66
+ - Load project file overlays (via `loadProjectConfig`).
67
+ - Load last-generated manifest overlays (via existing manifest loading).
68
+ - Report a new `project-file-drift` finding when the overlay sets differ.
69
+ - Suggest `regen` to reconcile.
70
+
71
+ ## Migration Considerations
72
+
73
+ | Repo state | Impact | Action |
74
+ | ----------------------------- | --------------------------------- | -------------------------------- |
75
+ | Has `superposition.yml` only | None — already correct | — |
76
+ | Has `superposition.json` only | `regen` errors | Run `cs migrate` once |
77
+ | Has both (consistent) | None — regen prefers project file | — |
78
+ | CI using `--from-manifest` | Warning on each run | Switch to `cs migrate` + `regen` |
79
+
80
+ ## Review & Approval Gate
81
+
82
+ Implementation proceeds with this spec committed. No further approval required for the
83
+ behavioral changes listed above.
@@ -0,0 +1,147 @@
1
+ # Feature Specification: Unified Project-Level Environment Variables
2
+
3
+ **Feature Branch**: `codex/project-env`
4
+ **Created**: 2026-03-29
5
+ **Status**: Implementing
6
+ **Input**: User request
7
+
8
+ ## Review & Approval _(mandatory before implementation)_
9
+
10
+ - **Spec Path**: `docs/specs/009-project-env/spec.md`
11
+ - **Commit Status**: Committed
12
+ - **Review Status**: Pending
13
+ - **Implementation Gate**: No implementation code may begin until this spec is committed and reviewed.
14
+
15
+ ## User Scenarios & Testing _(mandatory)_
16
+
17
+ ### User Story 1 — Define environment variables once for plain stack (Priority: P1)
18
+
19
+ A developer adds an `env:` block to `superposition.yml` on a `plain` stack and expects the
20
+ variables to appear in `devcontainer.json → remoteEnv` after regeneration.
21
+
22
+ **Why this priority**: The most common use case is a plain-stack project needing a handful of
23
+ environment variables visible inside the devcontainer without manually editing JSON files.
24
+
25
+ **Independent Test**: Create a `superposition.yml` with `stack: plain` and `env: {APP_NAME: my-app}`,
26
+ run `regen`, and confirm that `devcontainer.json` contains `"remoteEnv": {"APP_NAME": "my-app"}`.
27
+
28
+ **Acceptance Scenarios**:
29
+
30
+ 1. **Given** `superposition.yml` has `env: {APP_NAME: my-app}`, **When** generation runs with `stack: plain`, **Then** `devcontainer.json` includes `remoteEnv.APP_NAME: my-app`.
31
+ 2. **Given** `env:` uses the long form (`{value: "foo", target: auto}` — an object with a required `value` string and optional `target` routing hint), **When** generation runs with `stack: plain`, **Then** the variable is written to `remoteEnv` identical to the string shorthand.
32
+
33
+ ---
34
+
35
+ ### User Story 2 — Define environment variables once for compose stack (Priority: P1)
36
+
37
+ A developer adds an `env:` block to `superposition.yml` on a `compose` stack and expects the
38
+ variables to appear in `docker-compose.yml → services.devcontainer.environment` after regeneration.
39
+
40
+ **Why this priority**: Compose-stack users currently must split env configuration across
41
+ `customizations.devcontainerPatch.remoteEnv` and `customizations.dockerComposePatch`, which is
42
+ error-prone and hard to discover.
43
+
44
+ **Independent Test**: Create a `superposition.yml` with `stack: compose` and
45
+ `env: {DB_HOST: postgres}`, run `regen`, and confirm the variable appears under
46
+ `services.devcontainer.environment` in the generated `docker-compose.yml`.
47
+
48
+ **Acceptance Scenarios**:
49
+
50
+ 1. **Given** `superposition.yml` has `env: {DB_HOST: postgres}` on a compose stack, **When** generation runs, **Then** `docker-compose.yml` contains `services.devcontainer.environment.DB_HOST: postgres`.
51
+ 2. **Given** `target: composeEnv` is specified, **When** generation runs with `stack: plain`, **Then** generation errors with a clear message about compose-only targets.
52
+
53
+ ---
54
+
55
+ ### User Story 3 — Explicit target overrides auto-routing (Priority: P2)
56
+
57
+ A developer uses `target: remoteEnv` on a compose-stack project to force a variable into
58
+ `devcontainer.json` instead of `docker-compose.yml`.
59
+
60
+ **Why this priority**: Power users occasionally need fine-grained control over where a variable
61
+ lands, especially for VS Code-specific settings that must be in `remoteEnv`.
62
+
63
+ **Independent Test**: Set `target: remoteEnv` on a variable in a compose-stack project, run
64
+ `regen`, and confirm the variable is in `devcontainer.json → remoteEnv` (not in
65
+ `docker-compose.yml → services.devcontainer.environment`).
66
+
67
+ **Acceptance Scenarios**:
68
+
69
+ 1. **Given** `{value: "bar", target: remoteEnv}` in `env:` on a compose stack, **When** generation runs, **Then** the variable is written to `devcontainer.json → remoteEnv` only.
70
+
71
+ ---
72
+
73
+ ## Overview
74
+
75
+ Add a first-class `env` field to `superposition.yml` so a project can define
76
+ environment variables once and have them land in the correct generated output:
77
+
78
+ - `remoteEnv` for `plain` stacks
79
+ - `services.devcontainer.environment` in `docker-compose.yml` for `compose` stacks
80
+
81
+ This removes the need to split simple container environment variables across
82
+ `customizations.devcontainerPatch.remoteEnv` and
83
+ `customizations.dockerComposePatch`.
84
+
85
+ ## Project File Shape
86
+
87
+ ```yaml
88
+ env:
89
+ APP_NAME: my-app
90
+ API_BASE_URL:
91
+ value: ${API_BASE_URL:-http://localhost:3000}
92
+ target: auto
93
+ ```
94
+
95
+ Rules:
96
+
97
+ - `env` is a map keyed by variable name.
98
+ - A value may be:
99
+ - a string shorthand, equivalent to `{ value: "<string>", target: auto }`
100
+ - an object with:
101
+ - `value: string` required
102
+ - `target: auto | remoteEnv | composeEnv` optional, default `auto`
103
+
104
+ ## Generation Behavior
105
+
106
+ ### `target: auto`
107
+
108
+ - `plain` stack: write the variable to `devcontainer.json -> remoteEnv`
109
+ - `compose` stack: write the variable to
110
+ `docker-compose.yml -> services.devcontainer.environment`
111
+
112
+ ### Explicit targets
113
+
114
+ - `remoteEnv`: always write to `devcontainer.json -> remoteEnv`
115
+ - `composeEnv`: write to `docker-compose.yml -> services.devcontainer.environment`
116
+ and error on non-compose stacks
117
+
118
+ ### Precedence
119
+
120
+ - Project-level `env` is applied before `customizations.devcontainerPatch` and
121
+ `customizations.dockerComposePatch`.
122
+ - Custom patch files remain the escape hatch and may override project-level env.
123
+
124
+ ## Root `.env` Expansion Bridge
125
+
126
+ When a compose-targeted project env value references `${NAME}` or
127
+ `${NAME:-default}`, generation should preserve that expression in
128
+ `docker-compose.yml` and also mirror matching keys from the project root `.env`
129
+ into `.devcontainer/.env`.
130
+
131
+ This allows:
132
+
133
+ 1. the repository root `.env` to remain the human-edited source of truth
134
+ 2. Docker Compose to resolve variables from `.devcontainer/.env`
135
+ 3. generated `docker-compose.yml` to avoid embedding resolved secret values
136
+
137
+ Bridge rules:
138
+
139
+ - only referenced variable names are copied
140
+ - existing unrelated entries in `.devcontainer/.env` are preserved
141
+ - missing root variables are not invented; Docker Compose defaults still work
142
+
143
+ ## Non-Goals
144
+
145
+ - No new syntax for targeting arbitrary non-devcontainer services
146
+ - No replacement for advanced compose patches; those remain under
147
+ `customizations.dockerComposePatch`
@@ -0,0 +1,130 @@
1
+ # Feature Specification: Compose Env Materialization and Env Template Naming
2
+
3
+ **Feature Branch**: `codex/compose-env-materialization`
4
+ **Created**: 2026-03-29
5
+ **Status**: Implementing
6
+ **Input**: User request
7
+
8
+ ## Review & Approval _(mandatory before implementation)_
9
+
10
+ - **Spec Path**: `docs/specs/010-compose-env-materialization/spec.md`
11
+ - **Commit Status**: Committed
12
+ - **Review Status**: Pending
13
+ - **Implementation Gate**: No implementation code may begin until this spec is committed and reviewed.
14
+
15
+ ## User Scenarios & Testing _(mandatory)_
16
+
17
+ ### User Story 1 — Compose env values materialize into .devcontainer/.env (Priority: P1)
18
+
19
+ A developer sets concrete env values in `superposition.yml` on a `compose` stack and expects
20
+ them to be written to `.devcontainer/.env` (not embedded in `docker-compose.yml`) so that
21
+ secrets are not committed to source control inside generated YAML.
22
+
23
+ **Why this priority**: Embedding resolved values directly in `docker-compose.yml` would expose
24
+ secrets or host-specific values in generated files that are typically committed to source control.
25
+ Materializing them into `.devcontainer/.env` keeps generated config template-only.
26
+
27
+ **Independent Test**: Set `env: {SECRET_KEY: supersecret}` on a compose-stack project, run
28
+ `regen`, and confirm: (a) `docker-compose.yml` contains
29
+ `services.devcontainer.environment.SECRET_KEY: ${SECRET_KEY}`, (b) `.devcontainer/.env` contains
30
+ `SECRET_KEY=supersecret`, and (c) the literal value `supersecret` does not appear in
31
+ `docker-compose.yml`.
32
+
33
+ **Acceptance Scenarios**:
34
+
35
+ 1. **Given** `env: {API_KEY: abc123}` on a compose stack, **When** generation runs, **Then** `docker-compose.yml` has `API_KEY: ${API_KEY}` and `.devcontainer/.env` has `API_KEY=abc123`.
36
+ 2. **Given** `env: {NAME: ${NAME:-default}}`, **When** generation runs with a root `.env` that sets `NAME=prod`, **Then** `.devcontainer/.env` receives `NAME=prod`.
37
+ 3. **Given** `env: {NAME: ${NAME}}` and no root `.env` entry for `NAME`, **When** generation runs, **Then** `.devcontainer/.env` does not include `NAME=` and Docker Compose shell fallback still works.
38
+
39
+ ---
40
+
41
+ ### User Story 2 — Configure env template entries with clearly named project-file field (Priority: P1)
42
+
43
+ A developer updates their `superposition.yml` to use `customizations.envTemplate` (instead of
44
+ the previously named `customizations.environment`) and expects the generated `.env.example`
45
+ content to be identical to what the old field produced.
46
+
47
+ **Why this priority**: The existing `environment` key is misleading — it writes to `.env.example`
48
+ (a template), not to the runtime container environment. Renaming it clarifies intent and prevents
49
+ confusion with the new `env:` field.
50
+
51
+ **Independent Test**: Replace `customizations.environment` with `customizations.envTemplate` in a
52
+ project file, run `regen`, and confirm the generated `.env.example` is byte-for-byte identical to
53
+ the output produced with the old key.
54
+
55
+ **Acceptance Scenarios**:
56
+
57
+ 1. **Given** `customizations.envTemplate: {FOO: bar}`, **When** generation runs, **Then** `.devcontainer/.env.example` contains `FOO=bar`.
58
+ 2. **Given** `customizations.environment: {FOO: bar}` (deprecated alias), **When** generation runs, **Then** `.devcontainer/.env.example` still contains `FOO=bar` (backward-compatible).
59
+ 3. **Given** a project file using `environment`, **When** it is read by the project loader, **Then** a deprecation warning is emitted directing users to rename the key.
60
+
61
+ ---
62
+
63
+ ### User Story 3 — remoteEnv wiring for compose env entries (Priority: P2)
64
+
65
+ A developer on a compose stack expects that compose-targeted env variables are also accessible
66
+ via `${containerEnv:KEY}` in `devcontainer.json → remoteEnv` so VS Code settings and
67
+ extensions can reference them.
68
+
69
+ **Why this priority**: Without a `remoteEnv` entry, VS Code extensions that read `process.env`
70
+ at startup may not see the variable, even though the container process will.
71
+
72
+ **Independent Test**: Set a compose-stack env variable, run `regen`, and confirm
73
+ `devcontainer.json → remoteEnv` includes `KEY: ${containerEnv:KEY}` alongside the
74
+ `docker-compose.yml` entry.
75
+
76
+ **Acceptance Scenarios**:
77
+
78
+ 1. **Given** `env: {DB_URL: postgres://localhost/dev}` on a compose stack, **When** generation runs, **Then** `devcontainer.json` contains `remoteEnv.DB_URL: ${containerEnv:DB_URL}`.
79
+
80
+ ---
81
+
82
+ ## Overview
83
+
84
+ Refine project-file environment semantics so compose-based projects do not
85
+ embed resolved env values directly in generated `docker-compose.yml` or
86
+ `devcontainer.json`.
87
+
88
+ At the same time, rename the project-file field
89
+ `customizations.environment` to `customizations.envTemplate` to make its
90
+ purpose explicit: it writes template variables to `.env.example`, not runtime
91
+ container environment.
92
+
93
+ ## Behavior
94
+
95
+ ### `env:` on `stack: compose`
96
+
97
+ For compose-targeted project env entries:
98
+
99
+ 1. Materialize concrete values into `.devcontainer/.env`
100
+ 2. Write `docker-compose.yml -> services.devcontainer.environment.KEY: ${KEY}`
101
+ 3. Write `devcontainer.json -> remoteEnv.KEY: ${containerEnv:KEY}`
102
+
103
+ This keeps generated config free of resolved secret values while still making
104
+ the variables available inside the devcontainer.
105
+
106
+ ### Value Resolution
107
+
108
+ - literals are written as-is to `.devcontainer/.env`
109
+ - `${NAME}` resolves from the repository root `.env` when present
110
+ - `${NAME:-default}` resolves from the repository root `.env`, otherwise uses
111
+ the inline default
112
+ - unresolved `${NAME}` values are omitted from `.devcontainer/.env` so shell
113
+ environment fallback remains possible
114
+
115
+ ### `env:` on `stack: plain`
116
+
117
+ No change: values still land directly in `devcontainer.json -> remoteEnv`.
118
+
119
+ ## `customizations.envTemplate`
120
+
121
+ - `customizations.envTemplate` is the canonical project-file field for values
122
+ that should be written to `.env.example`
123
+ - `customizations.environment` is retained as a deprecated backward-compatible
124
+ alias
125
+ - serializers should emit `envTemplate`
126
+
127
+ ## Non-Goals
128
+
129
+ - no change to `.devcontainer/custom/environment.env`
130
+ - no support for targeting arbitrary compose sidecar services from `env:`
@@ -0,0 +1,235 @@
1
+ # Feature Specification: Overlay Parameters with Safe Substitution
2
+
3
+ **Feature Branch**: `011-overlay-parameters`
4
+ **Created**: 2026-03-30
5
+ **Status**: Final
6
+ **Input**: Issue — Introduce overlay parameters with safe, namespaced substitution — no conflicts with Docker/shell/VS Code
7
+
8
+ ## Review & Approval _(mandatory before implementation)_
9
+
10
+ - **Spec Path**: `docs/specs/011-overlay-parameters/spec.md`
11
+ - **Commit Status**: Committed
12
+ - **Review Status**: APPROVED
13
+ - **Implementation Gate**: No implementation code may begin until this spec is committed and reviewed.
14
+
15
+ ## Summary
16
+
17
+ Add first-class **parameters** to overlays so users can configure environment-specific values
18
+ (credentials, database names, ports, paths) without forking overlays or hand-editing generated
19
+ files.
20
+
21
+ Parameters use the `{{cs.PARAM_NAME}}` substitution syntax, which does not collide with Docker
22
+ Compose (`${VAR}`), shell (`$VAR`, `${VAR}`), VS Code (`${localWorkspaceFolder}`), or GitHub
23
+ Actions (`${{ }}`).
24
+
25
+ This is **parameter substitution only** — no loops, no conditionals, no embedded logic.
26
+ If `string.replace()` can't do it, it doesn't belong here.
27
+
28
+ ---
29
+
30
+ ## Design
31
+
32
+ ### Syntax
33
+
34
+ ```
35
+ {{cs.PARAM_NAME}}
36
+ ```
37
+
38
+ - **Safe**: does not collide with `${VAR}` (Docker/shell), `${env:VAR}` (VS Code), or `${{ }}` (GitHub Actions)
39
+ - **Consistent**: extends the existing `{{parameters.<key>.id}}` preset convention
40
+ - **Explicit**: clearly owned by container-superposition
41
+ - **Simple**: resolved by a single `string.replace()` regex, no parser needed
42
+
43
+ ### Overlay parameter declarations (`overlay.yml`)
44
+
45
+ Overlays declare parameters in `overlay.yml`:
46
+
47
+ ```yaml
48
+ id: postgres
49
+ name: PostgreSQL
50
+ category: database
51
+ parameters:
52
+ POSTGRES_DB:
53
+ description: Database name
54
+ default: app
55
+ POSTGRES_USER:
56
+ description: Database user
57
+ default: postgres
58
+ POSTGRES_PASSWORD:
59
+ description: Database password
60
+ default: postgres
61
+ sensitive: true
62
+ POSTGRES_PORT:
63
+ description: Host-mapped port
64
+ default: '5432'
65
+ ```
66
+
67
+ Fields:
68
+
69
+ - `description` (required) — human-readable explanation shown in interactive prompts
70
+ - `default` (optional) — default value; absence marks the parameter as _required_
71
+ - `sensitive` (optional, boolean) — indicates secrets; hidden in interactive prompts and redacted from plan output
72
+
73
+ ### Usage in overlay files
74
+
75
+ Overlay patches and compose files reference parameters using `{{cs.PARAM_NAME}}`:
76
+
77
+ ```json
78
+ {
79
+ "remoteEnv": {
80
+ "DATABASE_URL": "postgres://{{cs.POSTGRES_USER}}:{{cs.POSTGRES_PASSWORD}}@postgres:5432/{{cs.POSTGRES_DB}}"
81
+ }
82
+ }
83
+ ```
84
+
85
+ ```yaml
86
+ # docker-compose.yml — generation-time substitution coexists with Docker runtime substitution
87
+ services:
88
+ postgres:
89
+ environment:
90
+ POSTGRES_DB: '{{cs.POSTGRES_DB}}'
91
+ POSTGRES_USER: '{{cs.POSTGRES_USER}}'
92
+ POSTGRES_PASSWORD: '{{cs.POSTGRES_PASSWORD}}'
93
+ ports:
94
+ - '${POSTGRES_PORT:-{{cs.POSTGRES_PORT}}}:5432'
95
+ ```
96
+
97
+ ### Parameters in `superposition.yml`
98
+
99
+ ```yaml
100
+ overlays:
101
+ - postgres
102
+ - redis
103
+
104
+ parameters:
105
+ POSTGRES_DB: myapp
106
+ POSTGRES_USER: veggerby
107
+ REDIS_PORT: '6380'
108
+ ```
109
+
110
+ ### Resolution order (highest wins)
111
+
112
+ 1. CLI overrides (`--param POSTGRES_DB=foo`)
113
+ 2. Project file (`superposition.yml` `parameters:` section)
114
+ 3. Overlay defaults (`overlay.yml` `parameters[KEY].default`)
115
+
116
+ ### Validation rules
117
+
118
+ | Condition | Behaviour |
119
+ | ---------------------------------------------------------- | ------------------------------------- |
120
+ | Missing required parameter (no default, no value supplied) | **Hard error** before generation |
121
+ | Unknown parameter (not declared by any selected overlay) | **Warning** (proceed) |
122
+ | Unresolved `{{cs.*}}` in final output | **Hard error** (catch-all safety net) |
123
+
124
+ ### Pass-through guarantee
125
+
126
+ The substitution engine MUST NOT touch:
127
+
128
+ - Docker Compose expressions: `${VAR}`, `${VAR:-default}`, `$VAR`
129
+ - VS Code/devcontainer variables: `${localWorkspaceFolder}`, `${containerWorkspaceFolder}`, `${env:VAR}`
130
+ - GitHub Actions expressions: `${{ github.* }}`
131
+ - Shell variables in scripts: `$FOO`, `${FOO}`, `${FOO:-default}`
132
+
133
+ Only tokens matching exactly `{{cs.[A-Z0-9_]+}}` are substituted.
134
+
135
+ ---
136
+
137
+ ## Implementation Scope
138
+
139
+ ### Types (`tool/schema/types.ts`)
140
+
141
+ ```typescript
142
+ export interface OverlayParameterDefinition {
143
+ description: string;
144
+ default?: string;
145
+ sensitive?: boolean;
146
+ }
147
+ ```
148
+
149
+ Add to `OverlayMetadata`:
150
+
151
+ ```typescript
152
+ parameters?: Record<string, OverlayParameterDefinition>;
153
+ ```
154
+
155
+ Add to `ProjectConfigSelection`:
156
+
157
+ ```typescript
158
+ parameters?: Record<string, string>;
159
+ ```
160
+
161
+ Add to `QuestionnaireAnswers`:
162
+
163
+ ```typescript
164
+ overlayParameters?: Record<string, string>;
165
+ ```
166
+
167
+ ### Parameter engine (`tool/utils/parameters.ts`)
168
+
169
+ - `collectOverlayParameters(overlayIds, allOverlayDefs)` — collect all declared parameters from selected overlays with their defaults
170
+ - `resolveParameters(declared, supplied)` — apply resolution order, return resolved map and errors
171
+ - `substituteParameters(content, resolved)` — replace `{{cs.KEY}}` tokens in a string
172
+ - `validateFinalContent(content)` — error if any `{{cs.*}}` remain after substitution
173
+
174
+ ### Composer (`tool/questionnaire/composer.ts`)
175
+
176
+ After all overlay files are read and before they are written to disk:
177
+
178
+ 1. Collect parameter declarations from selected overlays
179
+ 2. Merge with `answers.overlayParameters` values
180
+ 3. Validate — error on missing required parameters
181
+ 4. Apply substitution to all file content strings (devcontainer.json, docker-compose.yml, .env.example, scripts)
182
+ 5. Validate — error on any unresolved `{{cs.*}}` tokens remaining in output
183
+
184
+ ### Project config (`tool/schema/project-config.ts`)
185
+
186
+ Parse `parameters:` YAML map as `Record<string, string>` (string values only).
187
+ Propagate to `selection.parameters` → `answers.overlayParameters`.
188
+
189
+ ### Init (`scripts/init.ts`)
190
+
191
+ When overlay declares parameters, interactive questionnaire prompts for values.
192
+ Sensitive parameters use masked input. Pre-filled with defaults.
193
+
194
+ ---
195
+
196
+ ## Non-goals
197
+
198
+ - Conditional logic (`{{if ...}}`)
199
+ - Loops or iteration
200
+ - Programmable overlays or JS execution
201
+ - Dynamic file generation
202
+ - Templating engine integration (Handlebars, Jinja, EJS, etc.)
203
+
204
+ ---
205
+
206
+ ## User Scenarios & Testing _(mandatory)_
207
+
208
+ ### User Story 1 — Postgres with custom database name (P1)
209
+
210
+ A user scaffolds a compose stack with the postgres overlay and wants their database named `myapp`
211
+ instead of the default `devdb`.
212
+
213
+ **Acceptance scenarios**:
214
+
215
+ 1. **Given** a `superposition.yml` with `parameters: { POSTGRES_DB: myapp }`, **When** generation runs, **Then** the generated `.devcontainer/docker-compose.yml` and `remoteEnv` in `devcontainer.json` reference `myapp` instead of `devdb`.
216
+ 2. **Given** an overlay with a required parameter (no default), **When** generation is run without providing the parameter value, **Then** the tool exits with a clear error message before writing any files.
217
+ 3. **Given** generated files contain no `{{cs.*}}` tokens, **When** output is validated, **Then** no error is raised and Docker Compose `${VAR}` expressions are preserved unmodified.
218
+ 4. **Given** a user runs `init` interactively with the postgres overlay, **When** the questionnaire reaches parameters, **Then** the user is prompted for each declared parameter with the default pre-filled.
219
+
220
+ ### User Story 2 — Sensitive parameter (P2)
221
+
222
+ A user provides a database password via parameter. The password must not appear in plan output in cleartext.
223
+
224
+ **Acceptance scenarios**:
225
+
226
+ 1. **Given** a parameter has `sensitive: true`, **When** the plan command shows parameter values, **Then** the value is displayed as `***`.
227
+ 2. **Given** a parameter has `sensitive: true`, **When** the interactive questionnaire prompts for it, **Then** the input is masked.
228
+
229
+ ### User Story 3 — Unknown parameter warning (P3)
230
+
231
+ A user adds a parameter in `superposition.yml` that is not declared by any selected overlay.
232
+
233
+ **Acceptance scenarios**:
234
+
235
+ 1. **Given** `parameters: { UNKNOWN_PARAM: foo }` in `superposition.yml`, **When** generation runs, **Then** a warning is printed but generation succeeds.
@@ -10,6 +10,7 @@ This directory contains reusable configuration fragments that can be imported by
10
10
  │ ├── instrumentation.env # OTEL SDK env vars for instrumentation
11
11
  │ └── otel-base-config.yaml # Base OTEL collector pipeline config
12
12
  ├── compose/ # Docker Compose patterns
13
+ │ ├── nvidia-gpu-devcontainer.yml # NVIDIA GPU passthrough for the devcontainer service
13
14
  │ └── common-healthchecks.md # Standard healthcheck patterns (reference — not importable)
14
15
  └── vscode/ # VS Code extension sets
15
16
  └── recommended-extensions.json # Commonly recommended extensions (devcontainer patch)
@@ -44,6 +45,20 @@ This directory contains reusable configuration fragments that can be imported by
44
45
 
45
46
  ---
46
47
 
48
+ ### `compose/nvidia-gpu-devcontainer.yml`
49
+
50
+ **Purpose:** Adds the `deploy.resources.reservations.devices` block to the `devcontainer` service, giving the devcontainer itself direct NVIDIA GPU access. This enables GPU-accelerated tooling (`torch`, `tensorflow`, CUDA CLIs, `nvidia-smi`) to work directly in the dev environment.
51
+
52
+ **Format:** Docker Compose service fragment (services.devcontainer only).
53
+
54
+ **Merge type:** `compose_imports:` — deep-merged into the final `docker-compose.yml` before the overlay's own `docker-compose.yml`.
55
+
56
+ **Imported by:** `ollama`
57
+
58
+ **Prerequisites:** NVIDIA Container Toolkit must be installed on the host.
59
+
60
+ ---
61
+
47
62
  ### `compose/common-healthchecks.md`
48
63
 
49
64
  **Purpose:** Reference library of standard Docker Compose healthcheck patterns for common services (HTTP, PostgreSQL, Redis, MongoDB, MySQL).
@@ -64,7 +79,7 @@ This directory contains reusable configuration fragments that can be imported by
64
79
 
65
80
  ## Usage
66
81
 
67
- Reference shared fragments in `overlay.yml` via the `imports` field:
82
+ Reference shared devcontainer fragments in `overlay.yml` via the `imports` field:
68
83
 
69
84
  ```yaml
70
85
  id: my-overlay
@@ -73,11 +88,21 @@ imports:
73
88
  - .shared/vscode/recommended-extensions.json
74
89
  ```
75
90
 
91
+ Reference shared docker-compose fragments via the `compose_imports` field:
92
+
93
+ ```yaml
94
+ id: my-overlay
95
+ compose_imports:
96
+ - .shared/compose/nvidia-gpu-devcontainer.yml
97
+ ```
98
+
76
99
  **Rules:**
77
100
 
78
101
  - All paths must begin with `.shared/`
79
102
  - Paths are relative to `overlays/`
80
- - Imports are applied in declaration order, then the overlay's own `devcontainer.patch.json` (overlay wins on conflict)
103
+ - `imports` fragments are applied in declaration order, then the overlay's own `devcontainer.patch.json` (overlay wins on conflict)
104
+ - `compose_imports` fragments are deep-merged into `docker-compose.yml` before the overlay's own `docker-compose.yml` (overlay wins on conflict)
105
+ - `compose_imports` files must be `.yml` or `.yaml`
81
106
 
82
107
  ## Creating New Fragments
83
108
 
@@ -0,0 +1,22 @@
1
+ # Shared Docker Compose fragment: NVIDIA GPU passthrough for the devcontainer service.
2
+ #
3
+ # Adds the deploy.resources.reservations.devices block to the devcontainer service so
4
+ # GPU-accelerated tooling (torch, tensorflow, CUDA CLIs, etc.) works directly in the
5
+ # dev environment alongside any GPU-enabled sidecar services.
6
+ #
7
+ # Requirements: NVIDIA Container Toolkit must be installed on the host.
8
+ # https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html
9
+ #
10
+ # Used by: ollama
11
+ # Import via compose_imports in overlay.yml:
12
+ # compose_imports:
13
+ # - .shared/compose/nvidia-gpu-devcontainer.yml
14
+ services:
15
+ devcontainer:
16
+ deploy:
17
+ resources:
18
+ reservations:
19
+ devices:
20
+ - driver: nvidia
21
+ count: all
22
+ capabilities: [gpu]