Skip to content
Draft
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
64 changes: 64 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,28 @@ jobs:
pnpm install --frozen-lockfile --ignore-scripts
printf '%s' "$PR_TITLE" | pnpm exec commitlint --config commitlint.config.cjs

validate-kit:
name: Validate kit
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install the sbx CLI
# `sbx kit validate` has no standalone distribution β€” pull the same linux-amd64 release
# tarball `sbx` itself ships (matches this runner's arch) and lift just the binary out.
env:
SBX_VERSION: "0.38.0"
run: |
curl -fsSL -o sbx.tar.gz \
"https://github.com/docker/sbx-releases/releases/download/v${SBX_VERSION}/DockerSandboxes-linux-amd64.tar.gz"
tar xzf sbx.tar.gz docker-sbx/sbx
sudo install docker-sbx/sbx /usr/local/bin/sbx

- name: Validate the kit
run: sbx kit validate kits/aidd

release-please:
name: Release Please
runs-on: ubuntu-latest
Expand Down Expand Up @@ -275,6 +297,48 @@ jobs:
"/tmp/${PLUGIN}-v${VERSION}.zip" \
--clobber

build-and-push-kit:
name: Build and push kit
needs: [release-please]
if: needs.release-please.outputs.release_created == 'true'
runs-on: ubuntu-latest
# packages: write β€” `sbx kit push` publishes an OCI artifact to ghcr.io.
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install the sbx CLI
env:
SBX_VERSION: "0.38.0"
run: |
curl -fsSL -o sbx.tar.gz \
"https://github.com/docker/sbx-releases/releases/download/v${SBX_VERSION}/DockerSandboxes-linux-amd64.tar.gz"
tar xzf sbx.tar.gz docker-sbx/sbx
sudo install docker-sbx/sbx /usr/local/bin/sbx

- name: Log in to GitHub Container Registry
# A dedicated PAT, not GITHUB_TOKEN: pushing a brand-new OCI artifact package via
# `sbx kit push` 403s on write_package with GITHUB_TOKEN, even though the same token
# pushes a container image fine elsewhere β€” GHCR's package-creation check does not
# extend to GITHUB_TOKEN the same way for a non-container OCI artifact. A PAT with the
# `write:packages` scope, stored as the GHCR_PACKAGES_TOKEN secret, sidesteps it.
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GHCR_PACKAGES_TOKEN }}

- name: Push the kit
# `sbx kit push` reads from the Docker credential store docker/login-action just
# populated for ghcr.io. Tagged both by the release version and `latest`.
run: |
VERSION="${{ needs.release-please.outputs.version }}"
IMAGE="ghcr.io/ai-driven-dev/aidd-sbx-kit"
sbx kit push kits/aidd "${IMAGE}:${VERSION}"
sbx kit push kits/aidd "${IMAGE}:latest"

publish-cli:
name: Publish cli
needs: [release-please]
Expand Down
2 changes: 2 additions & 0 deletions aidd_docs/memory/codebase-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ flowchart TD
Root["framework/"] --> Plugins["plugins/"]
Root --> Cli["cli/"]
Root --> Kanban["kanban/"]
Root --> Kits["kits/"]
Root --> Scripts["scripts/"]
Root --> Docs["docs/"]
Root --> AiddDocs["aidd_docs/"]
Expand All @@ -24,6 +25,7 @@ flowchart TD
| `plugins/` | the product β€” one dir per plugin, each with `plugins/<plugin>/.claude-plugin/plugin.json` and `skills/`, optionally `agents/`, `commands/`, `hooks/`, `rules/` |
| `cli/` | the `aidd` binary. Has its own memory bank and `CLAUDE.md` |
| `kanban/` | the task board. A private package with its own lockfile, unwired from the CLI |
| `kits/` | Docker Sandbox mixin kits that install the framework into a sandbox, native `claude plugin` calls, no CLI dependency. `kits/<name>/spec.yaml` |
| `scripts/` | repository checks and generators, run by lefthook and CI. Tests in `scripts/__tests__/` |
| `docs/` | durable docs β€” architecture, plugin authoring, glossary, maintainer runbook. `prompts-documentation.md` is generated |
| `aidd_docs/` | this memory bank, plus `tasks/`, `runs/`, `product/`, `specs/`, `recipes/`, `brainstorm/` |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
status: done
---

<!-- Fill or omit these sections; never add, rename, or reorder one. -->

# Instruction: Kit descriptor

## Architecture projection

> Tree of the final files. βœ… create Β· ✏️ modify Β· ❌ delete

```txt
.
└── kits/
└── aidd/
β”œβ”€β”€ spec.yaml βœ… create
└── README.md βœ… create
```

## User Journey

```mermaid
flowchart TD
A["sbx kit add <sandbox> ghcr.io/ai-driven-dev/aidd-sbx-kit:<version>"] --> B["sbx recreates the sandbox, applies kits/aidd/spec.yaml"]
B --> C["setup.install: guard on claude CLI, then marketplace add + plugin install loop, aidd-ui excluded"]
C --> D["setup.install: merge extraKnownMarketplaces + enabledPlugins into /etc/claude-code/managed-settings.json"]
D --> E["Session starts: managed-settings.json survives the runtime overwrite of ~/.claude/settings.json"]
E --> F["Agent sees AIDD plugins enabled, aidd-ui still disabled"]
```

## Test Scope

<!-- Required for every phase. Keep Setup, Happy path, any qualifying Edge cases, and any required Teardown in this one journey. -->

```mermaid
---
title: Test scope
---
journey
section Setup
Install the sbx CLI from the docker/sbx-releases tarball => sbx on PATH: 5: system
section Happy path
sbx kit validate kits/aidd => exits 0, no schema error: 5: cli
section Edge case - claude CLI absent in the sandbox image
setup.install runs with no claude on PATH => guard prints "aidd: this kit only applies to the claude-code agent" and exits 1, not a silent partial install: 1: system
```

## Wireframe

<!-- UI phase only. No UI => omit the section, don't invent one. -->

## Tasks to do

### `1)` Write the kit descriptor

> `kits/aidd/spec.yaml`, the mixin Docker Sandbox kit v2 descriptor.

1. `schemaVersion: "2"`, `kind: mixin`, `name: aidd`, `version: "1.0.0"`, `displayName`, `description`, `sourceURL: https://github.com/ai-driven-dev/framework`.
2. `permissions.network.allow: [github.com]` β€” the marketplace add clones over HTTPS.
3. `setup.install`, step one, run as the sandbox's non-root user: guard on `command -v claude`, exit 1 with a stated reason if absent; `claude plugin marketplace add ai-driven-dev/framework`; loop `claude plugin install "<plugin>@aidd-framework"` over `aidd-context`, `aidd-refine`, `aidd-dev`, `aidd-vcs`, `aidd-pm`, `aidd-orchestrator` β€” `aidd-ui` is not in this list.
4. `setup.install`, step two: merge `extraKnownMarketplaces` and `enabledPlugins` from the user's `~/.claude/settings.json` into `/etc/claude-code/managed-settings.json` (create both as `{}`/`/dev/null` when absent, never run the merge as root against the user file, preserve every other key already in the managed file).
5. `agentInstructions.content`: the AIDD SDLC primer (same text already proven in the installed kit instance under `kits-agent-context/aidd.md`).

### `2)` Write the kit's README

> `kits/aidd/README.md` β€” purpose, install command, limits.

1. State what the kit installs and that it targets Claude Code only.
2. Show `sbx kit add <sandbox> ghcr.io/ai-driven-dev/aidd-sbx-kit:<version>` and `sbx run <agent> --kit ./kits/aidd` for local use.
3. State the limit: a mixin kit, not a full sandbox template β€” it does not provide or replace the agent's own image.

### `3)` Validate the kit locally

> Prove the descriptor is well-formed before it ever reaches CI.

1. Install `sbx` the same way CI will: pull the pinned `docker/sbx-releases` linux-amd64 tarball, extract `docker-sbx/sbx`, install it on `PATH`.
2. Run `sbx kit validate kits/aidd`; fix the descriptor until it exits 0.

## Test acceptance criteria

<!-- Each criterion is an observable behavior, not a command. -->

| Task | Acceptance criteria |
| ---- | -------------------------------- |
| 1 | `kits/aidd/spec.yaml` parses as a valid Docker Sandbox kit v2 mixin; its install loop names six plugins and never names `aidd-ui` |
| 2 | `kits/aidd/README.md` names the install command and states the Claude-only, mixin-only scope |
| 3 | `sbx kit validate kits/aidd` exits 0 |
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
status: done
---

<!-- Fill or omit these sections; never add, rename, or reorder one. -->

# Instruction: Managed-settings regression test

## Architecture projection

> Tree of the final files. βœ… create Β· ✏️ modify Β· ❌ delete

```txt
.
└── scripts/
└── __tests__/
└── kit-aidd-managed-settings.test.js βœ… create
```

## User Journey

```mermaid
flowchart TD
A["Test reads kits/aidd/spec.yaml"] --> B["Extracts the install-loop plugin list and the managed-settings merge command verbatim"]
B --> C["Asserts aidd-ui is absent from the plugin list"]
C --> D["Runs the merge command against fixture managed.json + user.json in a temp dir"]
D --> E["Asserts the merged managed.json carries the AIDD marketplace and plugins, and keeps any pre-existing unrelated key"]
```

## Test Scope

<!-- Required for every phase. Keep Setup, Happy path, any qualifying Edge cases, and any required Teardown in this one journey. -->

```mermaid
---
title: Test scope
---
journey
section Setup
Create a temp dir with a fixture managed.json (holding one unrelated pre-existing key) and a fixture user.json (extraKnownMarketplaces + enabledPlugins) => fixtures ready: 5: system
section Happy path
Run the exact merge command parsed from kits/aidd/spec.yaml against the fixtures => managed.json gains extraKnownMarketplaces.aidd-framework and enabledPlugins with every installed plugin true, aidd-ui absent, the pre-existing key intact: 5: cli
section Edge case - managed.json does not exist yet
No managed.json in the temp dir => the command creates it as {} first => the merge still succeeds: 1: system
section Edge case - user settings file does not exist
No user.json in the temp dir => the merge is a no-op => managed.json is unchanged: 1: system
section Teardown
Remove the temp dir => no fixture left on disk: 5: system
```

## Wireframe

<!-- UI phase only. No UI => omit the section, don't invent one. -->

## Tasks to do

### `1)` Extract the live spec's install logic

> Pin the test against the real file, never a re-typed copy.

1. Parse `kits/aidd/spec.yaml` with `js-yaml` (already a dev dependency) inside the test.
2. Read `setup.install[0].command` and `setup.install[1].command` as plain strings β€” nothing else in the test re-implements them.

### `2)` Write and run the regression test

> `scripts/__tests__/kit-aidd-managed-settings.test.js`.

1. Assert `aidd-ui` is not among the plugins named in the install loop; assert the other six are.
2. In a fresh `mkdtemp` directory, write the fixture `managed.json` and `user.json` described in Test Scope.
3. Run the extracted merge command in that directory (substituting its hardcoded paths for the fixture paths), then read back `managed.json`.
4. Assert the merged shape and the two edge cases from Test Scope; clean up the temp directory.
5. Run via `node scripts/check-tests-leave-git-alone.js -- node --test 'scripts/__tests__/kit-aidd-managed-settings.test.js'`.

## Test acceptance criteria

<!-- Each criterion is an observable behavior, not a command. -->

| Task | Acceptance criteria |
| ---- | -------------------------------- |
| 1 | the test's assertions read from `kits/aidd/spec.yaml` itself, not from a duplicated string |
| 2 | the test fails if `aidd-ui` is ever added to the install loop, and passes against the current `spec.yaml` |
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
status: done
---

<!-- Fill or omit these sections; never add, rename, or reorder one. -->

# Instruction: CI wiring and docs

## Architecture projection

> Tree of the final files. βœ… create Β· ✏️ modify Β· ❌ delete

```txt
.
β”œβ”€β”€ .github/
β”‚ └── workflows/
β”‚ └── ci.yml ✏️ modify
β”œβ”€β”€ aidd_docs/
β”‚ └── memory/
β”‚ └── codebase-map.md ✏️ modify
β”œβ”€β”€ docs/
β”‚ └── ARCHITECTURE.md ✏️ modify
└── commitlint.config.cjs ✏️ modify
```

## User Journey

```mermaid
flowchart TD
A["Pull request touches kits/"] --> B["validate-kit job: install sbx, sbx kit validate kits/aidd"]
C["release-please merges a release on main"] --> D["build-and-push-kit job: install sbx, docker/login-action with GHCR_PACKAGES_TOKEN"]
D --> E["sbx kit push kits/aidd ghcr.io/ai-driven-dev/aidd-sbx-kit:<version> and :latest"]
E --> F["package visible under the ai-driven-dev GitHub org"]
```

## Test Scope

<!-- Required for every phase. Keep Setup, Happy path, any qualifying Edge cases, and any required Teardown in this one journey. -->

```mermaid
---
title: Test scope
---
journey
section Setup
Open the edited ci.yml => workflow file ready to inspect: 5: system
section Happy path
A pull request touching kits/ runs => validate-kit installs sbx and sbx kit validate kits/aidd exits 0: 5: ci
section Edge case - no release was created
release-please runs with no release_created output => build-and-push-kit does not run, and is not reported failed: 1: ci
section Edge case - GHCR_PACKAGES_TOKEN is missing
the secret is absent on the first real release => docker/login-action fails loudly with an auth error, not a silent skip: 1: ci
```

## Wireframe

<!-- UI phase only. No UI => omit the section, don't invent one. -->

## Tasks to do

### `1)` Add the PR-time validate job

> `validate-kit` in `.github/workflows/ci.yml`, runs on every pull request and push, independent of `release-please`.

1. Install `sbx` from the pinned `docker/sbx-releases` linux-amd64 tarball (same pin as phase 1's local check).
2. Run `sbx kit validate kits/aidd`.

### `2)` Add the release-time publish job

> `build-and-push-kit`, `needs: [release-please]`, `if: needs.release-please.outputs.release_created == 'true'`, mirroring `build-and-attach`'s gating.

1. `permissions: packages: write` on the job (matches the existing `publish-cli` job's grant for its GitHub Packages mirror).
2. Install `sbx` the same way as task 1.
3. `docker/login-action` against `ghcr.io` using a new repository secret `GHCR_PACKAGES_TOKEN` β€” a personal access token with `write:packages`, not `GITHUB_TOKEN`: pushing a brand-new non-container OCI artifact under `GITHUB_TOKEN` 403s on package creation. **This secret must exist before this job can succeed; creating it is an org-admin action outside this change's scope β€” state that plainly in the pull request rather than assuming it is already set.**
4. `sbx kit push kits/aidd ghcr.io/ai-driven-dev/aidd-sbx-kit:${{ needs.release-please.outputs.version }}` and again tagged `:latest`.

### `3)` Update the memory and architecture docs

> Keep the maps honest now that `kits/` exists.

1. Add a `kits/` row to `aidd_docs/memory/codebase-map.md`'s Areas table: holds the Docker Sandbox kit descriptors that install the framework into a sandbox.
2. Add one line to `docs/ARCHITECTURE.md` placing `kits/` relative to `plugins/`: a kit packages the CLI-free native install path for one external tool (Docker Sandboxes), not a Claude Code plugin.
3. Add `kit` to `commitlint.config.cjs`'s `scope-enum` list, kept kebab-case and alongside the other path-named scopes.

## Test acceptance criteria

<!-- Each criterion is an observable behavior, not a command. -->

| Task | Acceptance criteria |
| ---- | -------------------------------- |
| 1 | a pull request touching `kits/` runs `validate-kit` and it exits 0 against the current descriptor |
| 2 | `build-and-push-kit` runs only when `release-please` reports a release, and is absent from the run otherwise |
| 3 | `codebase-map.md` and `ARCHITECTURE.md` each name `kits/` and its relation to `plugins/` |
Loading