Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c30df1e
Add spec for feature workspace-runtime-contract-01KRC8WY
jonesrussell May 11, 2026
bf64bbb
Add meta for feature workspace-runtime-contract-01KRC8WY
jonesrussell May 11, 2026
75ebede
Add plan for feature workspace-runtime-contract-01KRC8WY
jonesrussell May 11, 2026
bf100f3
Add tasks for feature workspace-runtime-contract-01KRC8WY
jonesrussell May 11, 2026
6d1c36d
Add tasks for feature workspace-runtime-contract-01KRC8WY
jonesrussell May 11, 2026
3634d66
chore: planning artifacts for workspace-runtime-contract-01KRC8WY
jonesrussell May 11, 2026
19fa306
chore: WP01 claimed for implementation
jonesrussell May 11, 2026
1391d81
chore: Move WP01 to for_review on spec workspace [claude]
jonesrussell May 11, 2026
d7868a5
chore: Move WP01 to approved on spec workspace [claude]
jonesrussell May 11, 2026
3b5ea17
chore: planning artifacts for workspace-runtime-contract-01KRC8WY
jonesrussell May 11, 2026
03fbbfc
chore: WP02 claimed for implementation
jonesrussell May 11, 2026
0284df0
chore: Move WP02 to for_review on spec workspace [claude]
jonesrussell May 11, 2026
93bc0f1
chore: Move WP02 to approved on spec workspace [claude]
jonesrussell May 11, 2026
00bd4b9
chore: WP03 claimed for implementation
jonesrussell May 11, 2026
5288435
chore: Move WP03 to for_review on spec workspace [claude]
jonesrussell May 11, 2026
8541700
chore: Move WP03 to approved on spec workspace [claude]
jonesrussell May 11, 2026
a8759ba
feat(kitty/mission-workspace-runtime-contract-01KRC8WY): squash merge…
jonesrussell May 11, 2026
1109697
spec(kitty): cross-link mission to OpenSpec change-id
jonesrussell May 11, 2026
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
79 changes: 79 additions & 0 deletions kitty-specs/workspace-runtime-contract-01KRC8WY/data-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Data Model — Workspace Runtime Contract

This document enumerates the durable entities of the runtime contract. Each entity is one invariant (INV-1…INV-9) plus its attributes and the relationships that bind it to other invariants.

## Entities

### INV-1 — SysboxRuntime
- **Attributes:** `runtime = "sysbox-runc"`, `privileged = false`, `apparmor = unconfined`, `seccomp = unconfined`.
- **Source of truth:** `user-defined-web/template.tf` (`docker_container.workspace` block).
- **Host precondition:** Sysbox installed and registered as a Docker runtime.

### INV-2 — WorkspaceIdentity
- **Attributes:** username `coder`, UID `1000`, primary group `coder`, supplementary group `docker` (default GID `988`, overridable via `var.docker_gid`).
- **Established by:** image `usermod -l coder` + HCL `user = "coder"` + `group_add` + agent env `HOME=/home/coder`.

### INV-3 — SudoPosture
- **Attributes:** entry `coder ALL=(ALL) NOPASSWD:ALL`, sudoers file mode `0440`, `DEBIAN_FRONTEND` preserved.
- **Source of truth:** `image/Dockerfile` sudoers RUN block.
- **Threat-model assumption:** workspace boundary = Sysbox; in-container UID separation is not a defense.

### INV-4 — DockerdLifecycle
- **Attributes:** in-container daemon, started by agent (`sudo dockerd &`), log at `/tmp/dockerd.log`, socket at `/var/run/docker.sock`, socket widened to `chmod 666` after readiness, optional `/etc/docker/daemon.json` registry mirror.
- **Forbidden:** host docker socket mount; systemd-as-PID-1 path.

### INV-5 — VolumeModel
- **Attributes:**
- Home: host bind `/coder-workspaces/<owner>-<ws>` → `/home/coder`.
- DinD: named volume `coder-<owner>-<ws>-dind-cache` → `/var/lib/docker`.
- No other persistent mounts; no host docker.sock.
- **Persistence semantics:** both volumes survive container stop/start; home is host-managed; DinD is Docker-managed.

### INV-6 — HydrationModel
- **Attributes:** staging dir `/home/coder-files/` (outside the bind mount), policy "copy-if-missing, append-if-missing", first-boot marker `~/.bashrc absent`, every step idempotent.
- **Consumed files (today):** `WELCOME.txt`, `.vscode/settings.json`, `.gitconfig`, `.gitignore_global`. Drift D-2 enumerates files staged but uncopied.

### INV-7 — RoutingModel
- **Attributes:** `ddev-router` globally omitted, project web container binds `localhost:80`, single project per workspace, external access only via Coder reverse tunnel + `coder_app.ddev-web` (subdomain).
- **Deviation:** the `freeform` template runs ddev-router and has its own contract; this spec does not govern freeform.

### INV-8 — CleanupModel
- **Attributes:** stop tears down container (count-gated), preserves both volumes; destroy must remove `/coder-workspaces/<owner>-<ws>/` via `null_resource.workspace_cleanup` invoking host-side `coder-delete-workspace-dir`. Stop is graceful: `SIGINT`, `stop_timeout = 180s`, `destroy_grace_seconds = 60s`. Two shutdown hooks run `ddev poweroff` (agent + `coder_script` with `run_on_stop=true`).
- **Host precondition:** `/usr/local/bin/coder-delete-workspace-dir` installed with passwordless sudo for the daemon user.

### INV-9 — IdentitySourceModel
- **Authoritative source:** `coder_agent.env` injects `CODER_WORKSPACE_{ID,NAME,OWNER_NAME,OWNER_EMAIL}`.
- **Forbidden as source:** container `hostname` (it is `<ws-name>-<owner>`; container name is `coder-<id>`; mismatch makes hostname parsing unreliable).
- **Used by:** git config injection, DDEV project naming, dashboard metadata.

## Relationships

| From | Relation | To |
|------|----------|-----|
| INV-1 | enables safe execution of | INV-4 |
| INV-2 | required by | INV-3, INV-4, INV-5 |
| INV-3 | required by | INV-4 (dockerd launch), INV-5 (chown), INV-6 (hydration writes) |
| INV-4 | persists state via | INV-5 (`/var/lib/docker` volume) |
| INV-5 | shadows image content, necessitating | INV-6 |
| INV-6 | populates state in scope of | INV-2 (user-owned files) |
| INV-7 | exposed to users via | `coder_app.ddev-web` |
| INV-8 | depends on host-side helper named in | charter §"Important Constraints" |
| INV-9 | consumed by every step that names a workspace, including | INV-6 (git identity), INV-7 (DDEV project naming) |

## Invariant Coverage Matrix

| Invariant | Image enforces | HCL enforces | Script enforces |
|----------:|:--------------:|:------------:|:---------------:|
| INV-1 | | yes | |
| INV-2 | yes | yes | yes (chown) |
| INV-3 | yes | | yes (uses) |
| INV-4 | yes (binary) | yes (no socket mount) | yes (start) |
| INV-5 | | yes | |
| INV-6 | yes (stages) | | yes (hydrate) |
| INV-7 | yes (ddev install) | yes (coder_app) | (relies on DDEV global config) |
| INV-8 | | yes | yes (shutdown) |
| INV-9 | | yes (env) | yes (uses) |

## Drift Catalog (informational)

See `research.md` §"Known Drift" for D-1…D-6. They are **not** modeled as entities here; they are flagged anomalies that will become OpenSpec change proposals in their own missions.
54 changes: 54 additions & 0 deletions kitty-specs/workspace-runtime-contract-01KRC8WY/lanes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"version": 1,
"mission_slug": "workspace-runtime-contract-01KRC8WY",
"mission_id": "01KRC8WYGG1KFDFRDA92GCAFGB",
"mission_branch": "kitty/mission-workspace-runtime-contract-01KRC8WY",
"target_branch": "spec-workspace-runtime-contract",
"lanes": [
{
"lane_id": "lane-a",
"wp_ids": [
"WP01",
"WP02",
"WP03"
],
"write_scope": [
"kitty-specs/workspace-runtime-contract-01KRC8WY/meta.json",
"openspec/changes/add-workspace-runtime-contract/design.md",
"openspec/changes/add-workspace-runtime-contract/proposal.md",
"openspec/changes/add-workspace-runtime-contract/specs/workspace-runtime/spec.md",
"openspec/changes/add-workspace-runtime-contract/tasks.md"
],
"predicted_surfaces": [
"artifact-rendering",
"workspace"
],
"depends_on_lanes": [],
"parallel_group": 0
}
],
"computed_at": "2026-05-11T19:57:31.318291+00:00",
"computed_from": "dependency_graph+ownership",
"planning_artifact_wps": [],
"collapse_report": {
"events": [
{
"wp_a": "WP02",
"wp_b": "WP01",
"rule": "dependency",
"evidence": "WP02 depends on WP01"
},
{
"wp_a": "WP03",
"wp_b": "WP01",
"rule": "dependency",
"evidence": "WP03 depends on WP01"
}
],
"total_merges": 2,
"independent_wps_collapsed": 0,
"by_rule": {
"dependency": 2
}
}
}
14 changes: 14 additions & 0 deletions kitty-specs/workspace-runtime-contract-01KRC8WY/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"created_at": "2026-05-11T19:40:51.106806+00:00",
"friendly_name": "workspace runtime contract",
"mission_id": "01KRC8WYGG1KFDFRDA92GCAFGB",
"mission_number": 2,
"mission_slug": "workspace-runtime-contract-01KRC8WY",
"mission_type": "software-dev",
"openspec_change_id": "add-workspace-runtime-contract",
"slug": "workspace-runtime-contract-01KRC8WY",
"source_description": "Foundational workspace-runtime-contract spec; wraps OpenSpec change add-workspace-runtime-contract. Descriptive-first capture of nine runtime invariants (Sysbox, identity, sudo, dockerd, volumes, hydration, routing, cleanup, identity source).",
"target_branch": "spec-workspace-runtime-contract",
"vcs": "git",
"vcs_locked_at": "2026-05-11T19:58:02.673303+00:00"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{"mission": "software-dev", "payload": {"action": "research", "agent": "claude", "decision_kind": "step", "mission_state": "discovery", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-11T19:42:30.006862+00:00", "type": "MissionNextInvoked"}
{"mission": "software-dev", "payload": {"action": "specify", "agent": "claude", "decision_kind": "step", "mission_state": "specify", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-11T19:51:38.466826+00:00", "type": "MissionNextInvoked"}
{"mission": "software-dev", "payload": {"action": "plan", "agent": "claude", "decision_kind": "step", "mission_state": "plan", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-11T19:52:19.800712+00:00", "type": "MissionNextInvoked"}
{"mission": "software-dev", "payload": {"action": "tasks-outline", "agent": "claude", "decision_kind": "step", "mission_state": "tasks_outline", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-11T19:53:10.957267+00:00", "type": "MissionNextInvoked"}
{"mission": "software-dev", "payload": {"action": "tasks-outline", "agent": "claude", "decision_kind": "step", "mission_state": "tasks_outline", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-11T19:53:42.321882+00:00", "type": "MissionNextInvoked"}
{"mission": "software-dev", "payload": {"action": "tasks-outline", "agent": "claude", "decision_kind": "step", "mission_state": "tasks_outline", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-11T19:53:47.918194+00:00", "type": "MissionNextInvoked"}
{"mission": "software-dev", "payload": {"action": "tasks-packages", "agent": "claude", "decision_kind": "step", "mission_state": "tasks_packages", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-11T19:56:30.947002+00:00", "type": "MissionNextInvoked"}
{"mission": "software-dev", "payload": {"action": "tasks-finalize", "agent": "claude", "decision_kind": "step", "mission_state": "tasks_finalize", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-11T19:56:37.906018+00:00", "type": "MissionNextInvoked"}
{"mission": "software-dev", "payload": {"action": "implement", "agent": "claude", "decision_kind": "step", "mission_state": "implement", "result_input": "success", "wp_id": "WP01"}, "timestamp": "2026-05-11T19:57:39.984042+00:00", "type": "MissionNextInvoked"}
{"mission": "software-dev", "payload": {"action": "implement", "agent": "claude", "decision_kind": "step", "mission_state": "implement", "result_input": "success", "wp_id": "WP02"}, "timestamp": "2026-05-11T20:00:05.857224+00:00", "type": "MissionNextInvoked"}
123 changes: 123 additions & 0 deletions kitty-specs/workspace-runtime-contract-01KRC8WY/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Implementation Plan: Workspace Runtime Contract

**Branch**: `spec-workspace-runtime-contract` | **Date**: 2026-05-11 | **Spec**: `kitty-specs/workspace-runtime-contract-01KRC8WY/spec.md`
**Input**: Mission specification + descriptive-first contract synthesized from `image/Dockerfile`, `user-defined-web/template.tf`, and the inlined `startup_script`.

## Summary

Deliver the foundational `workspace-runtime-contract` as **proposal artifacts only**. The work consists of three coordinated outputs: (1) the Spec Kitty mission artifacts (spec.md, plan.md, research.md, data-model.md), already in flight; (2) the OpenSpec change `add-workspace-runtime-contract` with proposal, design, tasks, and a delta spec under `openspec/changes/add-workspace-runtime-contract/specs/workspace-runtime/spec.md`; (3) a cross-link layer joining the two systems via `meta.json` and the OpenSpec proposal body. No code under `image/`, `*/template.tf`, `*/scripts/`, or `Makefile` is touched.

## Technical Context

**Language/Version**: Markdown (specs), JSON (mission metadata), Terraform HCL (already in place — not modified).
**Primary Dependencies**: `spec-kitty-cli` 3.1.8, `openspec` CLI, `gh` CLI, `terraform` ≥ 1.5.
**Storage**: Filesystem (mission dir, openspec/changes dir), git (auto-committed by Spec Kitty + manual commits for OpenSpec).
**Testing**: `openspec validate add-workspace-runtime-contract --strict`; `terraform fmt -check -recursive`; mission `status.json` lane invariants.
**Target Platform**: WSL2 / Linux host running the agent; PR targets `ddev/coder-ddev:main` on GitHub.
**Project Type**: Documentation/governance change. No source code modifications.
**Performance Goals**: N/A.
**Constraints**: No edits outside `kitty-specs/workspace-runtime-contract-01KRC8WY/`, `openspec/changes/add-workspace-runtime-contract/`, and the eventual PR body. Draft PR only.
**Scale/Scope**: One foundational spec, nine invariants, six drift items, one OpenSpec change.

## Charter Check

Charter directive #1 (respect risk boundaries) identifies image, template, and startup-script changes as high-risk. This mission **does not modify** any of those surfaces; it only describes them. Charter directive #2 (keep documentation synchronized) is partially satisfied: the spec captures the current contract, but `CLAUDE.md` reconciliation is deferred to a follow-up change (FR-010). No charter gate is violated.

**Quality gates (from charter):**
- All changes via PR — satisfied.
- `terraform fmt -check` — satisfied (no HCL touched).
- `terraform validate` — satisfied (no HCL touched).
- `terraform test` — N/A (no HCL touched).
- Staging integration tests — N/A (no runtime behavior changes).

## Project Structure

### Documentation (this mission)

```
kitty-specs/workspace-runtime-contract-01KRC8WY/
├── spec.md # Mission specification (filled)
├── plan.md # This file
├── research.md # Decisions + drift catalog (filled)
├── data-model.md # INV-1…INV-9 entities + coverage matrix (filled)
├── research/
│ ├── evidence-log.csv # Invariant → file/line evidence (filled)
│ └── source-register.csv# Source files used in synthesis (filled)
├── tasks.md # WP manifest (next phase output)
├── wps.yaml # Work package list (next phase output)
├── lanes.json # Lane allocation (next phase output)
├── tasks/ # Per-WP prompt files (next phase output)
├── meta.json # Mission identity + OpenSpec cross-link
└── mission-events.jsonl # Append-only event log
```

### Change artifacts (OpenSpec — created during implement phase)

```
openspec/changes/add-workspace-runtime-contract/
├── proposal.md
├── design.md
├── tasks.md
└── specs/workspace-runtime/spec.md # The delta spec (ADDED Requirements x9)
```

**Structure Decision**: This is a *documentation-and-governance* mission. There is no source-code structure to choose. The two artifact homes (`kitty-specs/...` and `openspec/changes/...`) are joined by cross-references; no third location is introduced.

## Phases

### Phase 0 — Discovery (complete)
- Research artifacts populated from prior architectural analyses.
- Drift items D-1…D-6 cataloged as informational, not remediated here.

### Phase 1 — Specify (complete)
- Mission `spec.md` written with FR-001…FR-010 and C-001…C-005.
- Cross-link plan recorded in research (mission → OpenSpec change-id via `source_description` plus link in `proposal.md`).

### Phase 2 — Plan (this document)
- Confirm charter gates.
- Decide WP allocation (Phase 3 input).

### Phase 3 — Tasks (next)
The mission needs three work packages, each scoped to a non-overlapping artifact tree:

| WP | Title | Owned files | Depends on |
|----|-------|-------------|-----------|
| WP01 | Author OpenSpec proposal scaffold | `openspec/changes/add-workspace-runtime-contract/proposal.md`, `design.md`, `tasks.md` | — |
| WP02 | Author runtime-contract delta spec | `openspec/changes/add-workspace-runtime-contract/specs/workspace-runtime/spec.md` | WP01 |
| WP03 | Cross-link mission to OpenSpec + draft PR | `kitty-specs/workspace-runtime-contract-01KRC8WY/meta.json` (extend `source_description`), PR body | WP01, WP02 |

Each WP carries an FR ref set drawn from FR-001…FR-010 above. None modifies image, template, script, or Makefile.

### Phase 4 — Implement
- WPs executed sequentially (small dependency chain; parallelism not worthwhile).
- Each WP commits via Spec Kitty auto-commit on the mission branch.

### Phase 5 — Review and Accept
- `openspec validate add-workspace-runtime-contract --strict` must pass.
- `terraform fmt -check -recursive` must pass (defensive — no HCL touched, but enforced).
- Spec Kitty `accept` step validates lane state.

### Phase 6 — Merge (out of scope for this mission)
- A draft PR is opened against `upstream/main` (`ddev/coder-ddev:main`).
- Spec Kitty `merge` is **not** invoked — the PR remains draft until human reviewer approval.

## Complexity Tracking

| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| Two governance systems active (OpenSpec + Spec Kitty) | Repo already has both initialized; unifying would itself be a larger change | Forcing a single system now would block on a decision this mission is not authorized to make |
| Three WPs for a documentation-only mission | Each artifact tree must be edited independently; lane `write_scope` prevents overlap | Bundling would obscure provenance and break the lane model |

## Risks

- **Auto-commit conflicts** — Spec Kitty's `auto_commit: true` interacts with manual commits for OpenSpec artifacts. Mitigation: stage OpenSpec files explicitly before invoking Spec Kitty steps to keep auto-commits scoped.
- **CLI/project version drift** — already encountered (3.1.7 vs 3.1.8). Mitigation: upgrade resolved; document in research.
- **PR base** — branch was created from `upstream/main`; PR must target `ddev/coder-ddev:main`, not `jonesrussell/coder-ddev:main`. Mitigation: explicit `-R ddev/coder-ddev` in `gh pr create`.
- **Existing live mission** (`github-org-gated-signup-01KR1P4G`) is concurrent. Mitigation: separate branch (`spec-workspace-runtime-contract`) isolates auto-commits.

## Done When

- All three WPs in `done` or `approved` lane in `status.json`.
- `openspec validate add-workspace-runtime-contract --strict` is green.
- Draft PR open against `ddev/coder-ddev:main` with body referencing change-id and mission slug.
- No file under `image/`, `*/template.tf`, `*/scripts/`, or `Makefile` in the PR diff.
Loading
Loading