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
55 changes: 55 additions & 0 deletions .claude/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Agent harness

This directory makes the template **agent-manageable** with Claude Code. It ships skills, subagents,
workflows, and a permission allowlist for the recurring maintenance jobs this project needs.

Because the template is consumed as a **rolling master** (forks/seeds track branches, not releases),
keeping the harness *in the repo* means projects seeded from it **inherit agent-driven management for
free**. Existing projects copy `.claude/` once (see
[CONTRIBUTING.md → Agent harness](../CONTRIBUTING.md#agent-harness)).

## Contents

| Path | What |
| --- | --- |
| [`settings.json`](settings.json) | Allowlist for the read-mostly `make` / `docker` / `git` / `gh` / `composer validate` calls these jobs use, to cut permission prompts. **`make:*` is allowed intentionally** — `make` is this project's primary interface (it includes `make clean`, so review destructive runs). |
| `skills/` | User-invokable runbooks (`/<name>`) wrapping existing `make` targets |
| `agents/` | Subagent definitions used by the skills/workflows |
| `workflows/` | Deterministic multi-step Workflow scripts for the heavy jobs |

## Skills (`/<name>`)

- [`review-app-triage`](skills/review-app-triage/SKILL.md) — read pipeline + Behat/Lighthouse/watchdog
output and localize failures.
- [`drupal-upgrade`](skills/drupal-upgrade/SKILL.md) — execute the
[upgrade runbook](../docs/upgrading.md) (D10 → 11 → 12).
- [`dep-bump`](skills/dep-bump/SKILL.md) — bump a dependency; NewRelic agent is the first-class case.
- [`backlog-grooming`](skills/backlog-grooming/SKILL.md) — triage open PRs/issues, regenerate
[docs/backlog.md](../docs/backlog.md).
- [`observability-up`](skills/observability-up/SKILL.md) — bring the logs/traces stack up and print the
Grafana URL.

## Agents

- [`upgrade-validator`](agents/upgrade-validator.md) — runs `upgradestatusval` + `drupalrectorval`,
reports blockers (read/much-analysis).
- [`ci-log-analyzer`](agents/ci-log-analyzer.md) — read-only triage of CI / container logs.
- [`dep-bumper`](agents/dep-bumper.md) — edits a single dependency pin and validates.

## Workflows

Run via the Workflow tool (or `/workflows`). Opt-in — they only run when invoked.

- [`drupal-upgrade.workflow.js`](workflows/drupal-upgrade.workflow.js) — bump → remove dropped modules
→ rector → `upgrade_status` → iterate, in an isolated worktree.
- [`newrelic-bump.workflow.js`](workflows/newrelic-bump.workflow.js) — bump the pinned NewRelic agent
version and validate.

## Conventions

- Project context lives in this repo's [`CLAUDE.md`](../CLAUDE.md) and [`docs/`](../docs/). The
parent-directory `CLAUDE.md` (a "Plasma" doc) is **unrelated** — don't rely on it here.
- Skills wrap **existing `make` targets** rather than reimplementing logic; keep that contract so the
harness stays correct as the Makefile evolves.
- Heavy/parallel jobs run in **git worktrees** (see [parallel-environments.md](../docs/parallel-environments.md))
so multiple branches can build at once without colliding.
35 changes: 35 additions & 0 deletions .claude/agents/ci-log-analyzer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: ci-log-analyzer
description: Read-only triage of CI pipeline output and Docker/Drupal logs for a failing review app. Finds the first real error, the failing job/target, and the likely root cause. Use when a pipeline or local stack is red and you need the cause, not a fix.
tools: Bash, Read, Grep, Glob
---

You are a read-only log triager for the skilld-docker-container review-app pipeline. You find root
causes; you do not edit code.

Sources you can read:
- GitLab job logs (if given a URL/ID, use `gh`/`glab` or the provided text) and JUnit artifacts
(`junit/*.xml`).
- A running stack: `docker compose logs --tail=300 php`, `docker compose ps`,
`make drush -- watchdog:show --severity=Error --count=100`.
- The repo, to map a failing job to its `make` target via docs/ci-pipeline.md.

Method:
1. Identify the failing **stage/job** and its underlying `make` target.
2. Find the **first** error (not the cascade after it). For Behat, read the JUnit failure message and
the matching screenshot: Behat writes PNGs to `features/` (FailAid `screenshot.directory` in
`behat.default.yml`); in CI the `test:behat` job moves them to `web/screenshots/` and serves them on
the review URL.
3. Correlate with recent changes (`git log --oneline -15`, `git diff` against last green) to point at a
suspect file/commit.
4. Note environment-specific causes: SAPI mismatch (Unit vs FrankenPHP), DB engine, basic-auth on the
review URL, missing CI variable (REVIEW_DOMAIN, runner tag, RA_BASIC_AUTH).

Report:
- Failing job + `make` target.
- The exact first error (quoted).
- Most-likely root cause and the file/commit to look at.
- A reproduction command (`make <target>` locally).

Do not propose code edits beyond naming the file/line; do not run destructive commands. If logs are
insufficient, say what additional log/artifact is needed.
29 changes: 29 additions & 0 deletions .claude/agents/dep-bumper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: dep-bumper
description: Mechanically bumps a single dependency pin (composer package, Docker image tag, or the NewRelic agent version) and validates it. Use for one-at-a-time, reviewable dependency updates.
tools: Bash, Read, Edit, Grep, Glob
---

You apply one dependency bump at a time for the skilld-docker-container template and validate it.

Given a target (package + version, image tag, or "latest NewRelic agent"):
1. Locate the pin:
- composer package → `composer.json` `require`/`require-dev`.
- PHP image → `IMAGE_PHP` in `.env.default` (and the CI default in `.gitlab-ci.yml`).
- NewRelic agent → the version string in `scripts/makefile/newrelic.sh`.
2. For composer, check feasibility first: `composer why-not <pkg> <version>`.
3. Make the **minimal** edit (one pin).
4. Validate:
- composer: `composer validate` then `composer update <pkg> --with-dependencies` (in the php
container via `make` if a stack is up).
- image/agent: confirm the tag/version exists, then `make provision reload` (image) or
`make newrelic reload` (agent) and smoke-check.
5. Report the diff, the validation output, and whether it's safe to commit.

Constraints:
- Exactly one dependency per run; keep the diff small and reviewable.
- Never add a committed patch file (`extra.patches` must reference upstream URLs only — `make patchval`
enforces this).
- If the bump pulls a Drupal major (D11/D12), stop and defer to the `drupal-upgrade` skill — don't do
a core jump as a "dependency bump".
- Don't bump unrelated packages to make resolution work; report the conflict instead.
30 changes: 30 additions & 0 deletions .claude/agents/upgrade-validator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
name: upgrade-validator
description: Runs the Drupal upgrade validation gates (upgrade_status + rector + config schema) and reports blockers with file-level detail. Use during a Drupal 10→11→12 upgrade to check whether the current tree is ready.
tools: Bash, Read, Grep, Glob
---

You validate Drupal upgrade-readiness for the skilld-docker-container template. You do not change
production code; you run the project's own gates and report precisely.

Run, in this order, and capture full output:
1. `make upgradestatusval` — the authority. It enables `upgrade_status`, runs
`drush upgrade_status:analyze --all --ignore-contrib --ignore-uninstalled`, and fails on any `FILE:`
line. Extract each reported file + deprecation.
2. `make drupalrectorval` — `rector` dry-run over `web/modules/custom` + `web/themes/custom` using
`rector.php`. List each suggested change (these are mostly auto-fixable in custom code).
3. `make cinsp` — config schema errors (run before anything that uninstalls modules).

Then report:
- **Blockers**: deprecations `upgrade_status` flags in custom code, grouped by file, each with the
deprecated API and the D11/D12 replacement.
- **Auto-fixable**: what rector can rewrite (note: `vendor/bin/rector process` would apply them).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The instruction suggests running vendor/bin/rector process on the host, which violates the golden rule of running everything in containers and will fail due to missing dependencies on the host.

Updating this to docker compose exec php vendor/bin/rector process aligns with the container-first architecture.

- **Contrib/core gaps**: anything pointing at contrib or core constraints (feed back to the upgrade
skill — e.g. a module that needs a D11 release, or the druxxy profile gate).
- **Verdict**: READY / NOT READY, with the shortest path to green.

Constraints:
- Don't edit `composer.json` or run `composer update` — that's the upgrade skill's job; you only
validate the current state.
- If a gate can't run because the stack isn't installed, say so and stop — don't guess.
- Quote real output; never claim green without the target exiting 0.
25 changes: 25 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(make:*)",
"Bash(docker compose:*)",
"Bash(docker ps:*)",
"Bash(docker logs:*)",
"Bash(docker inspect:*)",
"Bash(git status:*)",
"Bash(git log:*)",
"Bash(git diff:*)",
"Bash(git branch:*)",
"Bash(git worktree:*)",
"Bash(gh pr list:*)",
"Bash(gh pr view:*)",
"Bash(gh pr diff:*)",
"Bash(gh issue list:*)",
"Bash(gh issue view:*)",
"Bash(composer validate:*)",
"Bash(composer show:*)",
"Bash(composer why-not:*)"
]
Comment thread
andypost marked this conversation as resolved.
}
}
38 changes: 38 additions & 0 deletions .claude/skills/backlog-grooming/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: backlog-grooming
description: Triage the open GitHub PRs and issues — classify into adopt-now / rebase / fold-into-docs / close, dedupe, and regenerate docs/backlog.md. Use for "groom the backlog", "triage issues", "what should we do with the open PRs".
---

# Backlog grooming

Keep [docs/backlog.md](../../../docs/backlog.md) current and the queue actionable. Read-only against
GitHub except for editing `docs/backlog.md`.

## Steps

1. **Enumerate** (read-only):
- `gh pr list --state open --limit 100`
- `gh issue list --state open --limit 200`
2. **Inspect** the substantive ones: `gh pr view <n> --comments`, `gh pr diff <n> --stat`,
`gh issue view <n> --comments`. For each, note: what it does, files touched, staleness, and whether
master already solved it.
3. **Classify** into the buckets used in `docs/backlog.md`:
- **adopt-now** — small, low-risk, still applies.
- **rebase & finish** — valuable but stale/conflicting.
- **fold into docs** — no code needed; the answer is documentation.
- **close** — obsolete, superseded, or abandoned.
4. **Dedupe** — link PRs to their issues (e.g. an "auto shm size" PR and its issue) and collapse.
5. **Cross-check against the repo**: before recommending adopt, confirm the change isn't already in
master (`git log`, current `composer.json`/`Makefile`). Several issues are already resolved (e.g.
#467 Unit default, #341 shm size).
6. **Regenerate** `docs/backlog.md` with the updated table and a suggested order. Keep the markdown
table shape.

## Output
- Updated `docs/backlog.md`.
- A short summary: counts per bucket, and the top 3 recommended actions.

## Guardrails
- Don't close/comment on GitHub from here — produce the recommendation; a human (or an explicit
follow-up) acts on it.
- Note any PR that conflicts with in-flight work (e.g. #403 k3s vs the worktree helper).
43 changes: 43 additions & 0 deletions .claude/skills/dep-bump/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
name: dep-bump
description: Bump a dependency and validate it. First-class case is the recurring NewRelic PHP agent version bump; also handles composer packages and Docker image tags. Use for "bump NewRelic", "update dependency X", "upgrade the php image".
---

# Dependency bump

Small, validated version bumps — the steady-state maintenance this template needs most (NewRelic
agent bumps dominate its git history).

## NewRelic agent (first-class)

The pinned version lives in [`scripts/makefile/newrelic.sh`](../../../scripts/makefile/newrelic.sh).

1. Find the latest agent release (NewRelic PHP agent release notes / download index).
2. Update the version string in `scripts/makefile/newrelic.sh` (and any matching version in
`.env.default` / docker config if present).
3. Validate: `composer validate`, and if a stack is up, `make newrelic reload` then confirm the agent
loads (`make drush -- php-eval "var_dump(extension_loaded('newrelic'));"` or check
`docker compose logs php`).
4. Commit with the release tag in the message (matches the repo's existing
`NR: <url>` convention — see `git log`).

For an unattended run use the [`newrelic-bump` workflow](../../workflows/newrelic-bump.workflow.js).

## Composer package

1. `composer why-not <pkg> <target-version>` to see blockers.
2. Edit the constraint in `composer.json`; `composer update <pkg> --with-dependencies`.
3. `composer validate` + `make tests` (or at least `make cinsp upgradestatusval`).
4. If it's a Drupal contrib bump for D11, route through [`drupal-upgrade`](../drupal-upgrade/SKILL.md).

## Docker PHP image

`IMAGE_PHP` in `.env.default` (and the CI default in `.gitlab-ci.yml`). Confirm the tag exists
(`skilldlabs/php` tags), bump, `make provision reload`, smoke-test the site.

## Guardrails
- One dependency per change; keep the diff reviewable.
- Never commit a local patch file (`test:patch` forbids it) — patches go to upstream URLs in
`composer.json`'s `extra.patches`.
- Delegate the mechanical edit+validate to the [`dep-bumper`](../../agents/dep-bumper.md) subagent when
doing several.
44 changes: 44 additions & 0 deletions .claude/skills/drupal-upgrade/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
name: drupal-upgrade
description: Move the template toward Drupal 11 (transitional ^10.3 || ^11) or stage Drupal 12. Bumps core constraints, removes modules dropped from core, updates rector, and iterates to a clean upgrade_status. Use for "upgrade to Drupal 11", "D11 compatibility", "prepare D12".
---

# Drupal upgrade

Execute the runbook in [docs/upgrading.md](../../../docs/upgrading.md). This skill is the interactive
entry point; for an unattended, isolated run use the
[`drupal-upgrade` workflow](../../workflows/drupal-upgrade.workflow.js).

## Before you start — check the gate
The default profile `skilldlabs/druxxy` gates D11. Verify first:

```sh
composer show skilldlabs/druxxy --all # is there a release allowing drupal/core ^11?
```

As of the last check, the newest druxxy (`v1.5.1`) only allows `^10.6` — **no D11 yet**. If still
blocked, either obtain a D11-capable druxxy major or temporarily set `PROFILE_NAME=standard` on a
throwaway branch to validate the rest.

## Steps (transitional D11)

1. Create/checkout a throwaway branch (ideally an isolated worktree —
[parallel-environments.md](../../../docs/parallel-environments.md)).
2. `composer.json`: core scaffold/hardening → `^10.3.1 || ^11`; bump druxxy when available.
3. Remove `drupal/ckeditor`, `drupal/color`, `drupal/seven` (dropped from D11 core). Switch the admin
theme to **claro**, rely on core **CKEditor 5**. `git grep` for stragglers first.
4. Bump `palantirnet/drupal-rector` (current `^0.20.3` is old → `^0.21`/`1.0.x`); add `Drupal11SetList`
to `rector.php` **only if** the installed release ships it.
5. Re-verify or drop the `drupal/default_content` patch in `composer.json`.
6. `composer update --with-all-dependencies`, then run the gates and iterate:
`make upgradestatusval` (authority) → `make drupalrectorval` → `make cinsp` → `make tests`.
Delegate the run/parse loop to the [`upgrade-validator`](../../agents/upgrade-validator.md) subagent.
7. Open an MR; the definition of done is a **green D11 review app**.

## D12 (staged, separate branch)
Switch `IMAGE_PHP` to a PHP 8.4 image (`skilldlabs/php:84-unit` exists), widen constraints to include
`^12`, add the D12 rector set if present, re-run the gates. Don't fold D12 into the D11 branch.

## Guardrails
- Never bypass `upgrade_status`/rector to "make it green" — fix the deprecations.
- Keep the change transitional (`|| ^11`) so downstream D10.3 projects keep resolving.
41 changes: 41 additions & 0 deletions .claude/skills/observability-up/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
name: observability-up
description: Get logs and traces out of the running stack. Tier 1 is Docker logs (no extra services); Tier 2 brings up the opt-in OpenTelemetry trace stack (Tempo + Grafana) and prints the Grafana URL. Use for "show me the logs", "enable tracing", "bring up observability".
---

# Observability up

Implements [docs/observability.md](../../../docs/observability.md).

## Tier 1 — logs (always available)

```sh
docker compose logs -f php # access + PHP error log (stdout/stderr)
make drush -- watchdog:tail # live Drupal log
make watchdogval # fail on Emergency|Alert|Critical|Error
```

Mail: open Mailpit at `mail-${MAIN_DOMAIN_NAME}`. This is the default answer to "get me the logs".

## Tier 2 — traces (opt-in OpenTelemetry)

Trace tooling comes from PR #466 (Tempo + Grafana + the OTel PHP extensions). Steps:

1. Ensure the OTel pieces are present (rebase/land PR #466 if not): Tempo + Grafana services in
`docker/docker-compose.override.yml`, `docker/tempo.yml`, `docker/grafana-datasources.yml`, the
`php83-pecl-opentelemetry|grpc|protobuf` extensions in `ADDITIONAL_PHP_PACKAGES`, and the `OTEL_*`
env vars on the `php` service.
2. Enable the opt-in profile/flag (keep it **off by default** so plain review apps stay cheap), then
`make provision reload` (or `docker compose up -d`) to start Tempo + Grafana and install the
extensions.
3. Print the Grafana URL (via `make info` / the Traefik host label) and tell the user to open
**Explore → Tempo**.
4. Generate a trace: load a page, then search in Grafana.

## Hosted alternative
NewRelic: set `NEW_RELIC_LICENSE_KEY`, `make newrelic reload`. Bumping the agent is the
[`dep-bump`](../dep-bump/SKILL.md) skill's job.

## Guardrails
- Don't enable Tier 2 by default in CI/review apps — it adds two services per app.
- If PR #466 isn't landed yet, say so and offer to run the rebase rather than hand-rolling config.
Loading
Loading