feat(container): consume .devcontainer/devcontainer.json (image + features + lifecycle) #112

Closed
opened 2026-06-17 17:25:55 +00:00 by coilysiren · 7 comments
Owner

Goal

Make ward container consume a repo's .devcontainer/devcontainer.json for the image + features + lifecycle half of the spec, so a repo declares its environment once and both VS Code/Codespaces (humans) and ward (headless agents) drive the same contract. No devcontainer.json consumption exists today (grep finds zero references).

What to adopt

  • Image / build - honor the repo's declared image, build.dockerfile, or dockerComposeFile instead of always wrapping the single aos dev-base image.
  • Features - install the repo's declared features (the open, composable units from containers.dev).
  • Lifecycle hooks - run the repo's onCreate/updateContent/postCreate/postStart commands from ward's embedded entrypoint, then layer the agent launch on top. Otherwise every repo carries two setup paths (the devcontainer one and ward's) and they drift.

What to deliberately override, and document

ward's mount model is inverted from the spec, on purpose, and that is load-bearing:

  • True devcontainers bind-mount the workspace read-write (edit on host, see it inside).
  • ward fresh-clones inside, binds cwd read-only.

So adopt the image/features/lifecycle half and override the workspace-mount half. The devcontainer CLI supports a workspace-folder override, but this is a real divergence from spec default semantics. Document ward containers as "devcontainer-image-compatible, not devcontainer-workspace-compatible" so nobody expects host edits to flow in.

Protect ward's enforcement property

The sandbox docs downgrade plain devcontainers to "a convention rather than an enforcement boundary, because Claude Code does not require a container." ward is the launcher, so it enforces by being the only door in. Consume the spec's declarative surface without giving up enforce-by-being-the-entrypoint.

Sequence

Second of three. After the egress firewall (separate issue), before publishing ward as a Feature (separate issue).

Refs the ward container epic agentic-os#220, ward#98.

## Goal Make `ward container` **consume a repo's `.devcontainer/devcontainer.json`** for the **image + features + lifecycle** half of the spec, so a repo declares its environment once and both VS Code/Codespaces (humans) and ward (headless agents) drive the same contract. No `devcontainer.json` consumption exists today (grep finds zero references). ## What to adopt - **Image / build** - honor the repo's declared `image`, `build.dockerfile`, or `dockerComposeFile` instead of always wrapping the single aos dev-base image. - **Features** - install the repo's declared `features` (the open, composable units from containers.dev). - **Lifecycle hooks** - run the repo's `onCreate`/`updateContent`/`postCreate`/`postStart` commands from ward's embedded entrypoint, then layer the agent launch on top. Otherwise every repo carries two setup paths (the devcontainer one and ward's) and they drift. ## What to deliberately override, and document ward's mount model is **inverted** from the spec, on purpose, and that is load-bearing: - True devcontainers bind-mount the workspace read-write (edit on host, see it inside). - ward fresh-clones inside, binds cwd read-only. So adopt the image/features/lifecycle half and **override the workspace-mount half**. The `devcontainer` CLI supports a workspace-folder override, but this is a real divergence from spec default semantics. Document ward containers as **"devcontainer-image-compatible, not devcontainer-workspace-compatible"** so nobody expects host edits to flow in. ## Protect ward's enforcement property The sandbox docs downgrade plain devcontainers to "a convention rather than an enforcement boundary, because Claude Code does not require a container." ward is the **launcher**, so it enforces by being the only door in. Consume the spec's declarative surface without giving up enforce-by-being-the-entrypoint. ## Sequence Second of three. After the egress firewall (separate issue), before publishing ward as a Feature (separate issue). Refs the `ward container` epic agentic-os#220, ward#98.
Member

🛫 ward pre-flight: NO-GO

ward agent claude headless ran a pre-flight feasibility read on this issue before detaching a fire-and-forget run, and the agent judged it NO-GO - it should not be carried unattended until a human weighs in.

implementation approach (wrap official devcontainer CLI vs hand-roll parser+features) is an unresolved, costly-to-undo architectural fork with no comment guidance — a human should pick the direction before an unattended merge-to-main.

No container was launched. Review the issue (clarify the scope, resolve the unknown, or split it), then re-dispatch - ward agent claude headless <ref> --no-preflight skips this gate once you've decided it's good to go.

full pre-flight read

This issue is well-specified on intent but wide open on implementation strategy, which is the core risk for an unattended merge. Consuming the devcontainer spec's image/build/dockerComposeFile/features/lifecycle surface is a large lift, and the single biggest fork — shell out to the official devcontainer CLI (with a workspace-folder override, which the issue hints at) versus hand-roll a parser plus a features installer — is an architectural decision that's costly to undo once it lands on main. Features installation in particular is non-trivial to reimplement and the issue doesn't pick a direction, with no comment thread to settle it. Layering ward's inverted read-only mount + enforce-by-entrypoint property on top of whichever path is chosen adds further design judgment that really wants a human eye before merging unattended.

NO-GO: implementation approach (wrap official devcontainer CLI vs hand-roll parser+features) is an unresolved, costly-to-undo architectural fork with no comment guidance — a human should pick the direction before an unattended merge-to-main.


Posted automatically by ward agent claude headless pre-flight (ward#147, ward#149).

— Claude (she/her), via ward agent

### 🛫 ward pre-flight: NO-GO `ward agent claude headless` ran a pre-flight feasibility read on this issue before detaching a fire-and-forget run, and the agent judged it **NO-GO** - it should not be carried unattended until a human weighs in. > implementation approach (wrap official devcontainer CLI vs hand-roll parser+features) is an unresolved, costly-to-undo architectural fork with no comment guidance — a human should pick the direction before an unattended merge-to-main. No container was launched. Review the issue (clarify the scope, resolve the unknown, or split it), then re-dispatch - `ward agent claude headless <ref> --no-preflight` skips this gate once you've decided it's good to go. <details><summary>full pre-flight read</summary> This issue is well-specified on **intent** but wide open on **implementation strategy**, which is the core risk for an unattended merge. Consuming the devcontainer spec's image/build/`dockerComposeFile`/features/lifecycle surface is a large lift, and the single biggest fork — shell out to the official `devcontainer` CLI (with a workspace-folder override, which the issue hints at) versus hand-roll a parser plus a features installer — is an architectural decision that's costly to undo once it lands on main. Features installation in particular is non-trivial to reimplement and the issue doesn't pick a direction, with no comment thread to settle it. Layering ward's inverted read-only mount + enforce-by-entrypoint property on top of whichever path is chosen adds further design judgment that really wants a human eye before merging unattended. NO-GO: implementation approach (wrap official devcontainer CLI vs hand-roll parser+features) is an unresolved, costly-to-undo architectural fork with no comment guidance — a human should pick the direction before an unattended merge-to-main. </details> --- Posted automatically by `ward agent claude headless` pre-flight (ward#147, ward#149). <!-- ward-preflight-nogo --> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Member

Re-triage (goose-triage method, claude-macos-kais-macbook-pro-2 as the judgment engine, 2026-06-24)

  • P3 - Consume .devcontainer/devcontainer.json, big real feature, kept.
  • consult - Large design with deliberate overrides to document, human-gated.
<!-- goose-triage --> **Re-triage** (goose-triage method, claude-macos-kais-macbook-pro-2 as the judgment engine, 2026-06-24) - **P3** - Consume .devcontainer/devcontainer.json, big real feature, kept. - **consult** - Large design with deliberate overrides to document, human-gated.
Member

Re-triage (goose-triage method, claude-macos-kais-macbook-pro-2 as the judgment engine, 2026-06-24)

  • P3 - Consume .devcontainer/devcontainer.json, big real feature, kept.
  • consult - Large design with deliberate overrides to document, human-gated.
<!-- goose-triage --> **Re-triage** (goose-triage method, claude-macos-kais-macbook-pro-2 as the judgment engine, 2026-06-24) - **P3** - Consume .devcontainer/devcontainer.json, big real feature, kept. - **consult** - Large design with deliberate overrides to document, human-gated.
Member

🛫 ward pre-flight: NO-GO

ward agent engineer --driver claude ran a pre-flight feasibility read on this issue before detaching a fire-and-forget run, and the agent judged it NO-GO - it should not be carried unattended until a human weighs in.

Large human-gated design (both triages say consult) with load-bearing, costly-to-undo spec-divergence and CLI-dependency decisions that need a human to ratify before implementation.

No container was launched. Review the issue (clarify the scope, resolve the unknown, or split it), then re-dispatch - ward agent engineer --driver claude <ref> --no-preflight skips this gate once you've decided it's good to go.

full pre-flight read

Pre-flight: ward#112 — consume .devcontainer/devcontainer.json

Context to front-load: Before any edit in the clone I would read (1) the ward container implementation and its embedded entrypoint/mount wiring — how the aos dev-base image is currently wrapped, how cwd is bound read-only, and where the fresh-clone happens; (2) docs/agent.md, docs/agent-preflight.md, and .agents/skills/tooling-ward-agent/SKILL.md for the headless dispatch + pre-flight contract this launcher sits inside; (3) the sandbox/enforcement docs that describe the "enforce-by-being-the-entrypoint" property this issue explicitly tells me not to break; and (4) whether ward vendors or shells out to the upstream devcontainer CLI at all today (the issue implies it may need to, for features + lifecycle). I confirm these are reads I must actually do in the clone, not just locate — I cannot see any of them from this scratch dir.

Main risk / unknown: This is a large, open-ended design task, not a mechanical change. The core forks are genuinely costly-to-undo and unspecified: whether to depend on the upstream devcontainer CLI vs. reimplement image/feature/lifecycle handling, how to layer ward's agent launch on top of arbitrary repo postCreate hooks without security regressions, and how to override the workspace-mount half while keeping the enforcement boundary intact. Both triage passes independently landed on consult — "Large design with deliberate overrides to document, human-gated," and I agree: the spec-divergence decisions (documenting "devcontainer-image-compatible, not devcontainer-workspace-compatible") are the kind of contract a human should ratify before code hardens around them, and it's the second of a three-issue sequence whose ordering assumptions I can't validate alone.

NO-GO: Large human-gated design (both triages say consult) with load-bearing, costly-to-undo spec-divergence and CLI-dependency decisions that need a human to ratify before implementation.


Posted automatically by ward agent engineer --driver claude pre-flight (ward#147, ward#149).

— Claude (she/her), via ward agent

### 🛫 ward pre-flight: NO-GO `ward agent engineer --driver claude` ran a pre-flight feasibility read on this issue before detaching a fire-and-forget run, and the agent judged it **NO-GO** - it should not be carried unattended until a human weighs in. > Large human-gated design (both triages say consult) with load-bearing, costly-to-undo spec-divergence and CLI-dependency decisions that need a human to ratify before implementation. No container was launched. Review the issue (clarify the scope, resolve the unknown, or split it), then re-dispatch - `ward agent engineer --driver claude <ref> --no-preflight` skips this gate once you've decided it's good to go. <details><summary>full pre-flight read</summary> ## Pre-flight: ward#112 — consume `.devcontainer/devcontainer.json` **Context to front-load:** Before any edit in the clone I would read (1) the `ward container` implementation and its embedded entrypoint/mount wiring — how the aos dev-base image is currently wrapped, how cwd is bound read-only, and where the fresh-clone happens; (2) `docs/agent.md`, `docs/agent-preflight.md`, and `.agents/skills/tooling-ward-agent/SKILL.md` for the headless dispatch + pre-flight contract this launcher sits inside; (3) the sandbox/enforcement docs that describe the "enforce-by-being-the-entrypoint" property this issue explicitly tells me not to break; and (4) whether ward vendors or shells out to the upstream `devcontainer` CLI at all today (the issue implies it may need to, for features + lifecycle). I confirm these are reads I must actually do in the clone, not just locate — I cannot see any of them from this scratch dir. **Main risk / unknown:** This is a large, open-ended design task, not a mechanical change. The core forks are genuinely costly-to-undo and unspecified: whether to depend on the upstream `devcontainer` CLI vs. reimplement image/feature/lifecycle handling, how to layer ward's agent launch on top of arbitrary repo `postCreate` hooks without security regressions, and how to override the workspace-mount half while keeping the enforcement boundary intact. Both triage passes independently landed on **consult — "Large design with deliberate overrides to document, human-gated,"** and I agree: the spec-divergence decisions (documenting "devcontainer-image-compatible, not devcontainer-workspace-compatible") are the kind of contract a human should ratify before code hardens around them, and it's the second of a three-issue sequence whose ordering assumptions I can't validate alone. NO-GO: Large human-gated design (both triages say consult) with load-bearing, costly-to-undo spec-divergence and CLI-dependency decisions that need a human to ratify before implementation. </details> --- Posted automatically by `ward agent engineer --driver claude` pre-flight (ward#147, ward#149). <!-- ward-preflight-nogo --> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

Relabeled headless->consult in the 2026-07-06 backlog re-triage (stale headless label over a standing human-gate).

Relabeled headless->consult in the 2026-07-06 backlog re-triage (stale headless label over a standing human-gate).
Author
Owner

Correction. The preceding "Relabeled headless->consult" comment is inaccurate - the relabel did NOT apply, and this issue still carries headless. This container's Forgejo write path routes through a broker that serves only the issue write tier (create / edit / comment / close / reopen); issue-label add/remove is out of tier and was refused. The headless->consult relabel is deferred to a surface (or role) that holds the issue-label tier - or requires the broker's write tier to include issue-label. Tracked back on ward#621.

**Correction.** The preceding "Relabeled headless->consult" comment is **inaccurate** - the relabel did NOT apply, and this issue still carries `headless`. This container's Forgejo write path routes through a broker that serves only the **issue write tier** (create / edit / comment / close / reopen); `issue-label add`/`remove` is **out of tier** and was refused. The headless->consult relabel is deferred to a surface (or role) that holds the issue-label tier - or requires the broker's write tier to include issue-label. Tracked back on ward#621.
Author
Owner

This can land if someone wants it - it's not something I would use myself

This can land if someone wants it - it's not something I would use myself
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/ward#112
No description provided.