Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions specs/clone.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ On `clone` create, the CLI MUST:
- **Reuse:** MUST NOT delete that workspace volume, including failure cleanup after create, start, ownership, populate, or hooks. MUST NOT run in-container git clone populate over an existing tree. If the mounted volume has no object entries other than `lost+found`, populate is allowed. If it has entries, skip populate and keep the tree; still apply author identity when `.git` exists; still overlay an edited `devcontainer.json` on `--resume`/recovery when that path already exists.
- **Replace:** delete-then-create. Failure cleanup MAY delete only the volume this invocation created after the operator chose replace.
- Config `type=volume` mounts remain list-then-create/reuse per Named volume reuse policy — no prompt. `rebuild` MUST remain unable to delete `*-ws`.
2. **`rebuild` carve-out:** `rebuild` of a volume-mode managed container MUST **reuse** the existing `*-ws` volume tree with its data and MUST NOT delete, replace, or re-populate it; MUST NOT run git re-clone or `git pull` inside it. The freshness rule applies to `clone` only.
2. **`rebuild` exception:** `rebuild` of a volume-mode managed container MUST **reuse** the existing `*-ws` volume tree with its data and MUST NOT delete, replace, or re-populate it; MUST NOT run git re-clone or `git pull` inside it. The freshness rule applies to `clone` only.
3. Mount that volume as the **container workspace folder** (the implicit workspace mount). MUST NOT bind-mount a durable host project directory as the workspace for clone-created containers.
4. **Existing occupant of the create name:** classify per **Create-name occupancy classification**. Same-workspace same-name MUST fail closed (MUST NOT silently reuse, replace, or attach; MUST NOT offer rename-to-duplicate). Foreign occupant MUST follow **Foreign create-name collision offer**. Same-workspace different-name MUST fail with a delete-hint.
5. Set labels on create:
Expand Down Expand Up @@ -360,7 +360,7 @@ before returning the structured failure. A reused pre-existing workspace volume

**Lifecycle (clone fresh create)**

At the start of `clone`, when a host checkout exists, the CLI MUST run host `initializeCommand` per [lifecycle-hooks.md](lifecycle-hooks.md) **initializeCommand host execution**. After successful populate, `clone` MUST run create-path lifecycle hooks with the **same matrix as `up` fresh create**:
At the start of `clone`, when a host checkout exists, the CLI MUST run host `initializeCommand` per [lifecycle-hooks.md](lifecycle-hooks.md) **initializeCommand host execution**. After successful populate, `clone` MUST run create-path lifecycle hooks in the **same order as `up` fresh create**:

`onCreateCommand` → `updateContentCommand` → `postCreateCommand` → `postStartCommand`

Expand Down Expand Up @@ -396,7 +396,7 @@ At the start of `clone`, when a host checkout exists, the CLI MUST run host `ini
- Then clone fails structured, the managed dev container is deleted, the workspace `*-ws` volume is deleted, and temps are cleaned up
- And a reused pre-existing workspace volume is not deleted

See also: [core.md](core.md) **Up lifecycle** and [lifecycle-hooks.md](lifecycle-hooks.md) for the shared create-path hook matrix; [vscode.md](vscode.md) for postAttach policy.
See also: [core.md](core.md) **Up lifecycle** and [lifecycle-hooks.md](lifecycle-hooks.md) for which hooks run on fresh create; [vscode.md](vscode.md) for postAttach policy.

---

Expand Down
14 changes: 7 additions & 7 deletions specs/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ After parse, the resolver MUST apply this substitution subset anywhere string va
| `${localWorkspaceFolderBasename}` | Basename of the workspace root |
| `${localEnv:VAR}` | Value of host environment variable `VAR` (empty string if unset, unless a default form is later specified) |
| `${containerWorkspaceFolder}` | Resolved container workspace folder path (after `workspaceFolder` resolution) |
| `${devcontainerId}` | Resource identity stem `adev-{base}-{hash12}` (empty resource base → `adev-{hash12}`). **Base** is the sanitized workspace folder basename (`up`) or git URL repo basename (`clone`) only — MUST NOT use config `name`. Hash material matches the product workspace volume (bind: workspace path + config path; volume: normalized git URL + config relative path). MUST NOT be the create `--name` / DNS hostname. Official meaning is unique + stable across rebuilds; this stem is that analogue. |
| `${devcontainerId}` | Resource identity stem `adev-{base}-{hash12}` (empty resource base → `adev-{hash12}`). **Base** is the sanitized workspace folder basename (`up`) or git URL repo basename (`clone`) only — MUST NOT use config `name`. Hash material matches the product workspace volume (bind: workspace path + config path; volume: normalized git URL + config relative path). MUST NOT be the create `--name` / DNS hostname. Official `${devcontainerId}` is unique and stable across rebuilds; this stem is the product's stand-in for that unique, rebuild-stable id. |

Unsupported substitution tokens MUST cause a structured error naming the token. Substitution MUST run before runtime admission and mount/port mapping.

Expand Down Expand Up @@ -377,7 +377,7 @@ The existing known optional families remain warn-and-ignore in default mode: doc
**Blocked inputs — Features-aware**

- `runArgs` entries not on the runArgs allowlist and not in the registered warn-and-ignore family
- First-class smuggling via runArgs (`-e`, `-u`, `-w`, `-p`, `-v`, …)
- First-class collisions via runArgs (`-e`, `-u`, `-w`, `-p`, `-v`, …)
- Docker Compose keys / compose-file driven multi-service config
- Unknown top-level dangerous properties; neither `image` nor nested `build`, or both together; invalid Feature option shapes; hostRequirements shortfalls; unsupported substitutions
- Top-level `dockerFile` / `dockerfile` / `context`
Expand Down Expand Up @@ -442,7 +442,7 @@ The existing known optional families remain warn-and-ignore in default mode: doc
- When config is validated
- Then admission succeeds; the docker-* ref is absent from admitted features; stderr warns naming the feature

#### Scenario: Non-ood features no longer rejected as blanket-unsupported
#### Scenario: Features other than docker-outside-of-docker are no longer rejected as blanket-unsupported
- Given `features` with only `ghcr.io/devcontainers/features/node:1`
- When config is validated at admission
- Then the CLI does not fail with a blanket “features are not supported” error
Expand Down Expand Up @@ -681,11 +681,11 @@ postAttach is **not** part of create-path delete-on-fail. Settings/open soft-fai
| Fresh create-path `up`/`clone`/`rebuild` with well-formed settings | after create-path hooks: settings merge (soft-fail); marker/idempotency rules |
| Fresh create-path without settings (and no pending payload) | no settings apply required |
| Any path with well-formed extensions, `--vscode` absent | extensions not installed by CLI on that invocation |
| Any path with well-formed extensions, `--vscode` set, marker pending/drift | before open: extensions install (soft-fail; flag gate only — runs even if open later soft-fails); then open; then postAttach only on open success per existing matrix |
| Any path with well-formed extensions, `--vscode` set, marker pending/drift | before open: extensions install (soft-fail; flag gate only — runs even if open later soft-fails); then open; then postAttach only on open success per the postAttach rows above |
| Any path with matching marker for full normalized payload | skip redundant settings+extensions apply |
| `start` / reuse with loadable config and marker drift | settings repair when applicable; extensions only when `--vscode` is set and still pending (not gated on open success) |

postAttach matrix rows and gating text above remain in force. Customizations apply is **not** part of create-path delete-on-fail and **not** folded into postAttach execution.
The postAttach rows in the lifecycle hook matrix above, and the gating text above, remain in force. Customizations apply is **not** part of create-path delete-on-fail and **not** folded into postAttach execution.

Create-path cleanup is unchanged: if any create-path hook fails before the command returns success, the CLI MUST delete the new/created container (extend to onCreate, updateContent, postCreate, and first-create postStart). On `rebuild`, delete-on-fail applies to the **new** container only (workspace/config volumes preserved); eligible hard post-delete failures then offer mode-split recovery.

Expand Down Expand Up @@ -745,7 +745,7 @@ Create-path cleanup is unchanged: if any create-path hook fails before the comma
- When the user runs `adevcontainer up` for that workspace
- Then the CLI fails with `config_hash_mismatch` and does not delete the container
- And the error hint mentions `adevcontainer rebuild` and managed selection (`--name` or auto)
#### Scenario: rebuild hook matrix row applies
#### Scenario: rebuild runs the fresh-create hook order
- Given a managed container being rebuilt with a config carrying initialize plus the four create-path hooks
- When `rebuild` runs the fresh create-path on the new container
- Then initialize runs on the host, then onCreate → updateContent → postCreate → postStart execute on the new container, and a first create-path hook failure deletes only the new container
Expand Down Expand Up @@ -1052,7 +1052,7 @@ The repository MUST provide pure JSON capability fixtures used by tests and docs
| `Tests/Fixtures/lifecycle.json` | postCreateCommand |
| `Tests/Fixtures/lifecycle-hooks.json` | lifecycle hooks |
| `Tests/Fixtures/runargs-host.json` | runArgs + hostRequirements |
| `Tests/Fixtures/features-node.json` | OCI Features runner (node only; no docker-ood) |
| `Tests/Fixtures/features-node.json` | OCI Features runner (node only; no docker-outside-of-docker) |
| `Tests/Fixtures/features-local.json` | Local path Features runner (sample-a + sample-b) |

Fixtures MUST be valid for their capability (no hard-error props such as Compose). They MAY include warn-skip surface when testing that path; ordinary fixtures SHOULD remain free of docker-* / privileged noise. They SHOULD align field styles with `reference/devcontainer.json` where applicable (image family, env keys, mount shapes, ports) while remaining Apple-container-runnable after warn-skips. Existing core fixtures MUST remain valid under lifecycle / runArgs / hostRequirements / Features-aware admission (configs without `features` behave as today).
Expand Down
10 changes: 5 additions & 5 deletions specs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ Warnings MUST be actionable via `StatusPrinter.warning` (or equivalent stderr pa
- When features are resolved for `up`
- Then the CLI does **not** fail solely for privileged/securityOpt; emits a warning; does not apply privileged/securityOpt to create; and MAY still install the feature

#### Scenario: Non-ood feature is not rejected solely as features-unsupported
- Given only `ghcr.io/devcontainers/features/node:1` in `features` (no ood)
#### Scenario: Non-docker-outside-of-docker feature is not rejected solely as features-unsupported
- Given only `ghcr.io/devcontainers/features/node:1` in `features` (no docker-outside-of-docker ref)
- When config is validated at the admission layer
- Then the CLI does **not** fail with the legacy “any features entry rejected / features runner post-MVP” policy

Expand Down Expand Up @@ -497,7 +497,7 @@ Privileged / `securityOpt` contributions are warn-stripped and not applied to cr
#### Scenario: up finish still has base-image postAttach after remelt
- Given Features apply already unioned base-image `postAttach` into the create config
- When `up` finish remelts feature postAttach from image metadata that is features-only
- Then the base-image postAttach still runs (remelt unions, does not replace-away)
- Then the base-image postAttach still runs (remelt unions; it does not drop the base-image hook)

#### Scenario: devcontainer.metadata label merge when present
- Given a base image with a parseable `devcontainer.metadata` label
Expand Down Expand Up @@ -528,7 +528,7 @@ Privileged / `securityOpt` contributions are warn-stripped and not applied to cr

### Requirement: Feature postStart remelt on resume

On every path that MUST run `postStartCommand` after a successful start of a previously stopped container (`up` start-stopped and bare `adevcontainer start` in bind and volume modes), the CLI MUST remelt feature-contributed `postStart` commands for that invocation. The CLI MUST run the config `postStartCommand` when present, then feature-contributed postStart commands, in the same merge/order spirit as create-path feature lifecycle hooks.
On every path that MUST run `postStartCommand` after a successful start of a previously stopped container (`up` start-stopped and bare `adevcontainer start` in bind and volume modes), the CLI MUST remelt feature-contributed `postStart` commands for that invocation — re-merge and run them again on this start, not only on the original create. The CLI MUST run the config `postStartCommand` when present, then feature-contributed postStart commands, in the same merge/order spirit as create-path feature lifecycle hooks.

Resume MUST NOT drop feature-contributed postStart solely because the container was created earlier. Feature onCreate / updateContent / postCreate MUST remain create-path only. A non-zero remelted feature postStart on resume MUST fail the command and MUST NOT delete the container.

Expand Down Expand Up @@ -591,7 +591,7 @@ The repository MUST provide:

| Path | Content |
|------|---------|
| `Tests/Fixtures/features-node.json` | Valid image-based config with **only** a non-ood OCI feature suitable for Node (e.g. `ghcr.io/devcontainers/features/node` with a pinned tag) and options if needed |
| `Tests/Fixtures/features-node.json` | Valid image-based config with **only** an OCI feature that is not docker-outside-of-docker, suitable for Node (e.g. `ghcr.io/devcontainers/features/node` with a pinned tag) and options if needed |
| `Tests/Fixtures/features-local.json` | Valid image-based config with local path features `./.devcontainer/features/sample-a` and `sample-b` (options as needed) |
| `Tests/Fixtures/features-sample/` | On-disk sample feature packages (`sample-a`, `sample-b`, `sample-privileged`) for unit + local E2E |

Expand Down
4 changes: 2 additions & 2 deletions specs/lifecycle-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Purpose

Lifecycle hook surface for `initializeCommand` through `postAttachCommand` (string | argv | object-map forms, parallel object-map, create-path order, resume/reuse behavior, delete-on-fail), plus `waitFor`, `userEnvProbe`, and `shutdownAction`. postAttach execution lives in [vscode.md](vscode.md); create-path matrices on `up` also appear in [core.md](core.md).
Lifecycle hook surface for `initializeCommand` through `postAttachCommand` (string | argv | object-map forms, parallel object-map, fresh-create (create-path) hook order, resume/reuse behavior, delete-on-fail), plus `waitFor`, `userEnvProbe`, and `shutdownAction`. postAttach execution lives in [vscode.md](vscode.md); the hooks that run on fresh create, resume, and reuse of `up` are also specified in [core.md](core.md) **Up lifecycle**.

## Requirements

Expand Down Expand Up @@ -310,4 +310,4 @@ This requirement MUST NOT change hook order, admitted forms, fail/delete-on-fail
- When the CLI executes that hook
- Then `==> Running …` status lines are not printed and the hook’s output still appears on host stderr as framed internal tool lines

See also: [core.md](core.md) **Up lifecycle** for the create/reuse/start path matrix (including postAttach and vscode customizations rows); [vscode.md](vscode.md) for **postAttachCommand policy (CLI-only)**; [features.md](features.md) **Feature postStart remelt on resume** and **Features progress status lines** for remelt and StatusPrinter / QUIET / `--json` norms; [terminal-output.md](terminal-output.md) for framing/color/QUIET presentation.
See also: [core.md](core.md) **Up lifecycle** for which hooks run on create, reuse, and start (including the postAttach and vscode customizations rows); [vscode.md](vscode.md) for **postAttachCommand policy (CLI-only)**; [features.md](features.md) **Feature postStart remelt on resume** and **Features progress status lines** for re-merging feature postStart on resume and for StatusPrinter / QUIET / `--json` norms; [terminal-output.md](terminal-output.md) for framing/color/QUIET presentation.
6 changes: 3 additions & 3 deletions specs/runargs-host.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Each skipped entry MUST emit a stderr warning naming the entry and that it is ig

**Still hard-error**

- First-class smuggling flags (`-e`/`-u`/`-w`/`-p`/`-v`/… listed above)
- First-class collisions: runArgs flags for first-class properties (`-e`/`-u`/`-w`/`-p`/`-v`/… listed above); hard-error even if that property is unset
- Any other flag or bare token not consumed by the allowlist rules above
- Incomplete valued forms (e.g. bare `--cap-add` with no name)

Expand All @@ -66,7 +66,7 @@ Errors MUST name the offending `runArgs` entry and state allowlist or first-clas
- When config is resolved and a create request is built
- Then admission succeeds and create argv includes `--init` and the corresponding `--cap-add` / `--cap-drop` forms supported by the runtime mapper

#### Scenario: Wave A+B allowlisted flags admit and map
#### Scenario: shm-size, dns, tmpfs, named network, and --rosetta/--ssh/--read-only admit and map
- Given `runArgs` including `--shm-size=64m`, `--dns=8.8.8.8`, `--tmpfs=/tmp:rw`, `--network=mynet`, `--rosetta`, `--ssh`, `--read-only`
- When config is resolved and a create request is built
- Then admission succeeds; create argv includes `--shm-size`/`64m`, `--dns`/`8.8.8.8`, `--tmpfs`/`/tmp` (opts stripped), `--network`/`mynet`, `--rosetta`, `--ssh`, `--read-only`
Expand Down Expand Up @@ -186,7 +186,7 @@ The repository MUST provide pure JSON fixtures for lifecycle, runArgs, and hostR
| `Tests/Fixtures/lifecycle-hooks.json` | `onCreateCommand`, `updateContentCommand`, `postCreateCommand`, `postStartCommand`; MAY include admitted `postAttachCommand` |
| `Tests/Fixtures/runargs-host.json` | Allowlisted `runArgs` and parseable `hostRequirements` (`memory` and/or `cpus`) |

Ordinary fixtures MUST NOT include Compose or unknown/first-class-smuggling runArgs (hard-error). Privileged/device family entries are valid warn-skip inputs (see warn-skip scenarios); keep default fixtures free of them unless exercising that path. They SHOULD remain Apple-container-runnable for optional integration tests.
Ordinary fixtures MUST NOT include Compose, unknown runArgs, or runArgs flags for first-class properties (hard-error even if that property is unset). Privileged/device family entries are valid warn-skip inputs (see warn-skip scenarios); keep default fixtures free of them unless exercising that path. They SHOULD remain Apple-container-runnable for optional integration tests.

#### Scenario: Lifecycle / runArgs / hostRequirements fixtures admit
- Given each lifecycle / runArgs / hostRequirements fixture file
Expand Down
2 changes: 1 addition & 1 deletion specs/terminal-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ On `clone`, after the container is created and started, the **in-container full
4. Keep host stdout pure under `--json`.
5. Under `ADEVCONTAINER_QUIET=1`, still emit framed tool body; only the product populate status/info lines are silenced.

This requirement extends presentation only; populate auth matrix, verify `.git`, and failure cleanup remain as in [clone.md](clone.md).
This requirement extends presentation only; populate auth by URL scheme, verify `.git`, and failure cleanup remain as in [clone.md](clone.md) **In-container full clone populate (auth by URL scheme)**.

#### Scenario: Clone populate streams live framed tool output
- Given a clone path whose in-container git clone prints progress lines to stdout/stderr and quiet mode unset
Expand Down
Loading
Loading