Skip to content
Open
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
80 changes: 80 additions & 0 deletions .agents/skills/create-runtime/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
name: create-runtime
description: Use when a user asks to add a new DevBox runtime image for an operating system, language, or framework, including its base image, project template, smoke test, and conformance validation.
---

# Create a DevBox Runtime

Create a complete runtime contribution, not just an empty directory. The result must be explainable from the repository's existing conventions and verified by the repository's own checks.

## Required input

Obtain these three values:

- `type`: `operating-system`, `language`, or `framework`
- `name`: the runtime directory name
- `version`: the runtime directory version

If any value is missing, ask for it before touching files. Run the input validator before creating a target:

```bash
python3 .agents/skills/create-runtime/scripts/validate-runtime-input.py \
--repo-root . \
--type <type> \
--name <name> \
--version <version>
```

Do not continue if the validator fails. It protects against malformed names, path traversal, and overwriting an existing runtime.

## Workflow

1. Read [references/common-runtime-contract.md](references/common-runtime-contract.md).
2. Read exactly one type guide:
- OS: [references/operating-systems.md](references/operating-systems.md)
- language: [references/languages.md](references/languages.md)
- framework: [references/frameworks.md](references/frameworks.md)
3. Inspect at least one nearby runtime of the same type. Prefer the closest runtime by base image, process model, and template shape; do not copy its version-specific facts.
4. Establish the facts that determine the implementation:
- official source and exact version;
- supported architectures;
- required base image and its existing image/version convention;
- installed tools and environment variables;
- process, entrypoint, port, and health behavior;
- project template commands and expected user-facing documentation.
5. If a required fact is unavailable or ambiguous, stop and ask a focused question. Do not guess.
6. Create the base image only when the new runtime needs one that is not already available. Create the runtime image, project template, localized documentation, and smoke test required by the selected type guide.
7. Register the exact runtime path in `tests/runtime-conformance/run.sh` and document its runtime-specific checks in `tests/runtime-conformance/README.md`; the conformance runner intentionally rejects unregistered paths.
8. Keep changes scoped to the new runtime. Do not modify existing runtimes, CI workflows, or shared tooling unless the new runtime requires a separately justified shared change.
9. Run the checks in the common contract and the selected type guide. Report every check that was skipped and why.

## No fallback without confirmation

Never silently:

- replace an exact version with `latest`;
- use an unverified mirror, package source, or base image;
- reduce architecture coverage because the current machine differs;
- omit a project template, localized README, or smoke test;
- replace a failed build or test with a weaker check;
- copy a neighboring runtime's behavior when its assumptions do not apply.

If the only way forward is a fallback, describe the trigger, the proposed behavior, and its impact, then wait for explicit user confirmation.

## Completion criteria

Before reporting completion, show evidence for:

- the validator accepted the target;
- all required files exist and executable scripts have the right mode;
- Dockerfiles and shell scripts pass available static checks;
- runtime conformance passes for the new target;
- the runtime path is registered in the conformance runner with assertions appropriate to its type;
- the matching smoke test passes, or its environmental blocker is reported precisely;
- no unrelated files were changed.

The skill creates repository files only. It does not publish images, push branches, or open pull requests unless the user separately asks for those actions.

## Example

For a new Python runtime, use `type=language`, `name=python`, and the requested exact `version`; inspect an existing Python runtime, add the matching base/runtime/template/smoke files, then run the validator and the repository's Python runtime checks. Do not derive installation details from the example's version.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Common Runtime Contract

Use this guide for every new runtime.

## Repository layout

- `tooling/`: shared build tools and scripts.
- `base-images/`: reusable base image definitions.
- `runtime-images/`: final images exposed as DevBox runtimes.
- `tests/runtime-smoke/`: smoke tests aligned with runtime image paths.
- `tests/runtime-conformance/`: repository-wide runtime checks.
- `docs/`: contributor and runtime behavior documentation.

A runtime Dockerfile belongs at:

```text
runtime-images/<kind>/<name>/<version>/Dockerfile
```

where `<kind>` is exactly `operating-systems`, `languages`, or `frameworks`.

## Inspect before editing

Use an existing runtime of the same kind as a structural reference. Check its:

- Dockerfile ARG and image naming conventions;
- `build.sh` ownership, locale, template, and dependency behavior;
- project-template files and localized README names;
- entrypoint protocol and default working directory;
- matching smoke test assertions;
- base image dependency, if any.

Copy structure only. Re-derive source URLs, checksums, versions, commands, ports, and package lists for the new runtime.

## Files and scripts

- Preserve executable mode for `build.sh` and `smoke.sh` when neighboring runtimes do. The conformance runner requires `project-template/entrypoint.sh` to exist and invokes it with `bash`; source executable mode is not itself a conformance requirement.
- Keep shell scripts strict (`set -euo pipefail` where compatible) and quote variables.
- Keep user-facing documentation in both `README.en_US.md` and `README.zh_CN.md` when the runtime has a project template.
- Make the template's documented commands match the actual entrypoint and build behavior.
- Do not add placeholder files to satisfy a path check.

## Verification

Use the repository's planner to verify that the target is discoverable and to inspect its CI matrix:

```bash
python3 .github/scripts/runtime-conformance.py plan \
--tag <tag> \
--kind <operating-systems|languages|frameworks> \
--name <name>/<version> \
--l10n both \
--arch both
```

The planner does **not** execute conformance. Before claiming conformance support, add the new runtime's exact relative path to the appropriate `case` branch in `tests/runtime-conformance/run.sh`, implement the runtime-specific assertions there, and update `tests/runtime-conformance/README.md`. The runner fails unregistered runtimes intentionally.

The supported published-image execution path is the `runtime-image-conformance.yaml` workflow: it pulls each already-published architecture/l10n image, mounts the repository at `/repo`, and runs. It does not build a new image.

```bash
bash /repo/tests/runtime-conformance/run.sh
```

For a not-yet-published runtime, build the image locally first, then run that same command inside the built runtime image with `RUNTIME_PATH`, `RUNTIME_DOCKERFILE`, `RUNTIME_IMAGE`, `L10N`, `CONFORMANCE_ARCH`, and `REPO_ROOT=/repo` set as the workflow does. The matching `tests/runtime-smoke/<kind>/<name>/<version>/smoke.sh` is also required by the smoke workflow and must be executed in the image as `devbox`. If Docker, registry access, or a required tool is unavailable, report the exact blocker; do not weaken the check silently.

## Scope boundary

Do not change CI workflows, shared tooling, or existing runtime files to hide a new runtime's failure. If a shared fix is genuinely required, stop and present that separate change explicitly.
33 changes: 33 additions & 0 deletions .agents/skills/create-runtime/references/frameworks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Framework Runtime Guide

Use for `type=framework`.

## Structure

Create the matching framework paths:

```text
base-images/frameworks/<name>/<version>/ # only when a new base is needed
runtime-images/frameworks/<name>/<version>/
tests/runtime-smoke/frameworks/<name>/<version>/smoke.sh
```

Framework runtimes commonly consume an existing language runtime. The standard contract requires `project-template/README.en_US.md`, `project-template/README.zh_CN.md`, and a `project-template/entrypoint.sh` unless the framework is an explicitly registered sandbox exception. Reuse an existing language image only after confirming the exact image name, tag, architecture support, and build contract.

## Required decisions

Determine and document:

- the framework's exact version and supported language/runtime versions;
- the package manager and lockfile strategy;
- dependency installation and production build commands;
- default port and bind address;
- production entrypoint and process lifetime;
- environment variables required by the framework;
- whether the project template is a minimal runnable service or a static/configuration template.

Do not copy a neighboring framework's dependency or startup command unless the framework's own documentation and template support it.

## Verification focus

The smoke test should install or build the minimal template as the image does, start the documented production process, and verify the expected service behavior. Check that the runtime image references a real language/base image and that its version convention matches the repository's image naming rules.
32 changes: 32 additions & 0 deletions .agents/skills/create-runtime/references/languages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Language Runtime Guide

Use for `type=language`.

## Structure

Create the matching language paths:

```text
base-images/languages/<name>/<version>/ # only when a new base is needed
runtime-images/languages/<name>/<version>/
tests/runtime-smoke/languages/<name>/<version>/smoke.sh
```

For the standard runtime contract used by this repository, include `project-template/README.en_US.md`, `project-template/README.zh_CN.md`, and a `project-template/entrypoint.sh` unless the runtime is an explicitly documented sandbox exception. The entrypoint or startup file must match the language's actual execution model.

## Required decisions

Determine and document:

- the exact compiler/interpreter distribution and version;
- official download or package source and verification data;
- architecture support;
- required compiler, package manager, runtime, and environment variables;
- whether the template compiles before running, installs dependencies, or runs directly;
- the process port and how the smoke test observes it.

Do not use an interpreted-language startup command for a compiled language, or infer package-manager behavior from another language.

## Verification focus

The smoke test should verify the exact toolchain version or a stable version constraint, compile/install the minimal template when required, start the documented service, and check the expected response or process behavior. Keep the test deterministic and aligned with the README.
31 changes: 31 additions & 0 deletions .agents/skills/create-runtime/references/operating-systems.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Operating System Runtime Guide

Use for `type=operating-system`.

## Structure

Inspect a nearby OS runtime and create the corresponding layers as required:

```text
base-images/operating-systems/<name>/<version>/
runtime-images/operating-systems/<name>/<version>/
tests/runtime-smoke/operating-systems/<name>/<version>/smoke.sh
```

The final runtime must include `project-template/README.en_US.md`, `project-template/README.zh_CN.md`, and a `project-template/entrypoint.sh` for the standard non-sandbox contract. Confirm any exception from an explicitly registered conformance branch before creating files.

## Required decisions

Determine and document:

- the official OS image or package source and exact release;
- amd64/arm64 support and any package availability differences;
- default shell, user, home, workdir, locale, timezone, and common utilities;
- SSH or other services required by the repository's OS contract;
- how the project entrypoint is invoked and how it stays alive for the smoke test.

Do not assume a distribution's package manager, init system, or architecture support from its name.

## Verification focus

The smoke test should prove the OS identity, required `devbox` user and project directory, required common commands, template files, and entrypoint behavior. Add only assertions supported by the runtime contract; do not copy language-specific checks into an OS runtime.
105 changes: 105 additions & 0 deletions .agents/skills/create-runtime/scripts/validate-runtime-input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""Validate create-runtime skill input and resolve its repository targets."""

from __future__ import annotations

import argparse
import json
import re
import sys
from pathlib import Path


TYPE_TO_KIND = {
"operating-system": "operating-systems",
"language": "languages",
"framework": "frameworks",
}
COMPONENT_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._+-]*$")


def fail(message: str) -> None:
print(f"Error: {message}", file=sys.stderr)
raise SystemExit(1)


def parse_component(label: str, value: str) -> str:
component = value.strip()
if not component:
fail(f"{label} cannot be empty")
if component in {".", ".."} or "/" in component or "\\" in component:
fail(f"{label} must be a single path component")
if "\x00" in component or not COMPONENT_PATTERN.fullmatch(component):
fail(f"{label} contains unsupported characters")
return component


def ensure_within(root: Path, target: Path) -> None:
try:
target.relative_to(root)
except ValueError:
fail(f"resolved target escapes repository root: {target}")


def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Validate create-runtime inputs without creating files"
)
parser.add_argument("--repo-root", required=True, type=Path)
parser.add_argument("--type", required=True, dest="runtime_type")
parser.add_argument("--name", required=True)
parser.add_argument("--version", required=True)
return parser


def main() -> int:
args = build_parser().parse_args()
repo_root = args.repo_root.expanduser().resolve()
if not repo_root.is_dir():
fail(f"repository root does not exist: {repo_root}")

try:
kind = TYPE_TO_KIND[args.runtime_type.strip()]
except KeyError:
fail("type must be one of: " + ", ".join(TYPE_TO_KIND.keys()))

name = parse_component("name", args.name)
version = parse_component("version", args.version)

runtime_dir = repo_root / "runtime-images" / kind / name / version
base_dir = repo_root / "base-images" / kind / name / version
smoke_test = (
repo_root
/ "tests"
/ "runtime-smoke"
/ kind
/ name
/ version
/ "smoke.sh"
)
for target in (runtime_dir, base_dir, smoke_test):
ensure_within(repo_root, target.resolve())

if runtime_dir.exists():
fail(f"runtime target already exists: {runtime_dir.relative_to(repo_root)}")
if smoke_test.exists():
fail(
"smoke test target already exists: "
f"{smoke_test.relative_to(repo_root)}"
)

payload = {
"type": args.runtime_type.strip(),
"kind": kind,
"name": name,
"version": version,
"runtime_path": runtime_dir.relative_to(repo_root).as_posix(),
"base_path": base_dir.relative_to(repo_root).as_posix(),
"smoke_test_path": smoke_test.relative_to(repo_root).as_posix(),
}
print(json.dumps(payload, ensure_ascii=False, sort_keys=True))
return 0


if __name__ == "__main__":
sys.exit(main())
Loading