Commit Graph

441 Commits

Author SHA1 Message Date
github-actions[bot] c82f51a3b8 chore: version packages (#223)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.10.1 dtu-github-actions@0.10.1
2026-04-12 15:44:12 +02:00
Peter Pistorius 71a3ebbfb5 fix: exclude test files from published dist (#222)
Test files (*.test.ts) were being compiled into dist/ and included in
the npm package. Add tsconfig exclude to keep them out of the build.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:43:01 +02:00
Peter Pistorius 9d1c0f4031 fix: format markdown files and auto-format after changeset version (#221)
The release workflow failed because changeset-generated CHANGELOGs weren't
formatted. Fix the existing files and run oxfmt after `changeset version`
so future Version Packages PRs include formatted markdown.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@redwoodjs/agent-ci@0.10.0 dtu-github-actions@0.10.0
2026-04-12 15:35:23 +02:00
Peter Pistorius c8c2b6cbcd docs: update compatibility.md to reflect current behavior (#199)
* docs: update compatibility.md to reflect current behavior

- GITHUB_TOKEN: no longer auto-injected, requires --github-token or AGENT_CI_GITHUB_TOKEN
- github.sha: now uses real commit SHA from HEAD, not hardcoded zeros
- secrets: documents env var fallback and --github-token auto-population

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: update github.sha to reflect dirty-tree SHA support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 12:32:52 +02:00
github-actions[bot] be2d64e734 chore: version packages (#205)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-12 12:32:26 +02:00
Peter Pistorius 93f0417098 fix: service containers leak on unclean shutdown (#217)
* fix: service containers leak on unclean shutdown

Problem: Service containers (e.g. `agent-ci-*-svc-cache-db`) were created
without the `agent-ci.pid` label, making them invisible to the bootstrap
orphan cleanup (`killOrphanedContainers`). When the parent process died
without hitting its signal handler (crash, SIGKILL, Bash tool timeout),
these containers leaked permanently. Additionally, SIGHUP was not handled,
so closing a tmux pane or SSH disconnect also leaked runner containers.
Containers stuck in `created` state were also missed by the prune logic.

Solution: Label service containers with `agent-ci.pid` at creation time.
Update `killOrphanedContainers` to clean up unlabeled containers (handles
pre-fix containers) and derive the runner name from svc container names
(strip `-svc-*` suffix) with deduplication. Add SIGHUP to signal handlers
in both `cli.ts` and `local-job.ts`. Extend `pruneOrphanedDockerResources`
to also prune `status=created` containers.

Fixes #216

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add Docker integration smoke tests for orphan cleanup

Spawn real Docker containers (unlabeled, dead-PID-labeled, alive-PID-labeled)
and verify killOrphanedContainers handles each case correctly against the
real Docker daemon. Tests are skipped when Docker is unavailable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: correct import path in orphan cleanup smoke test

The test spawns a subprocess via `npx tsx -e` which imports
shutdown.ts. The path was relative to the repo root but vitest
runs with cwd=packages/cli/, causing MODULE_NOT_FOUND in CI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 12:22:01 +02:00
Peter Pistorius 7a06aec813 fix: setup-bun cache collision (#219)
* test: smoke repro for setup-bun cache collision (#212)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: setup-bun fails in bun projects (#212)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 12:21:49 +02:00
Peter Pistorius 38994c80cf fix: use XDG cache on Linux Docker Desktop so bind mounts work (#218)
* fix: service containers leak on unclean shutdown

Problem: Service containers (e.g. `agent-ci-*-svc-cache-db`) were created
without the `agent-ci.pid` label, making them invisible to the bootstrap
orphan cleanup (`killOrphanedContainers`). When the parent process died
without hitting its signal handler (crash, SIGKILL, Bash tool timeout),
these containers leaked permanently. Additionally, SIGHUP was not handled,
so closing a tmux pane or SSH disconnect also leaked runner containers.
Containers stuck in `created` state were also missed by the prune logic.

Solution: Label service containers with `agent-ci.pid` at creation time.
Update `killOrphanedContainers` to clean up unlabeled containers (handles
pre-fix containers) and derive the runner name from svc container names
(strip `-svc-*` suffix) with deduplication. Add SIGHUP to signal handlers
in both `cli.ts` and `local-job.ts`. Extend `pruneOrphanedDockerResources`
to also prune `status=created` containers.

Fixes #216

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use XDG cache on Linux Docker Desktop so bind mounts work (#215)

`/tmp` is not in Docker Desktop's default shared-folder list on Linux,
so bind mounts from `/tmp` silently fail. Detect Linux Docker Desktop
via `~/.docker/desktop/docker.sock` and fall back to `$XDG_CACHE_HOME`
(or `~/.cache`). Also update the validate command to run agent-ci in
the background with Monitor for proper pause-on-failure support, and
note Docker Desktop as a supported Linux provider in the README.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove stop hook that ran agent-ci synchronously

The stop hook can't support pause-on-failure since it runs after the
conversation ends. Use `/validate` instead, which runs agent-ci in the
background with Monitor support for fixing and retrying failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 11:02:35 +02:00
Peter Pistorius 1c7e663327 fix: use /var/run/docker.sock as bind mount when user is not in docker group (#209)
Fixes #209 — on Linux with Docker Desktop active, agent-ci failed at
runner container creation with "mounts denied" when the user isn't in
the docker group.

Splits socketPath (API client, needs R/W) and bindMountPath (container
mount source, only needs path recognition by Docker's VM) as
independent decisions. Whenever /var/run/docker.sock exists on the
host, use it as bindMountPath regardless of our process's R/W access.
Collapses #197 (macOS Desktop symlink) and #209 (Linux Desktop
non-docker-group) into one rule.

The working-directory half of the original #210 report is tracked
separately in #215.

Closes #209

Co-Authored-By: Bastiaan Steinmeier <bastiaan.steinmeier@broekhuis.nl>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:51:36 +02:00
Peter Pistorius cf185857df perf: hoist docker cleanup and image prefetch to session bootstrap (#213)
Global Docker/workspace cleanup and runner-image prefetch now run once
per session instead of once per workflow. Also dedup concurrent
ensureImagePulled calls so parallel workflows share a single in-flight
docker pull. Together these eliminate the N× daemon storm that was
serializing `agent-ci run --all`.

Per-job ensureImagePulled calls remain as a safety net and take the
inspect() fast path since images are already warm.

Fixes #211

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 21:58:40 +02:00
Peter Pistorius 46c4d1457c fix: drop markdown from lint-staged format check
Auto-generated CHANGELOGs from changesets don't match oxfmt's
markdown formatting, which breaks the release workflow when
changesets/action tries to commit version bumps through husky.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 20:27:56 +02:00
Peter Pistorius 66ac2a4b6b test: smoke repro for missing cc in runner image (#207)
* test: add smoke repro for missing cc in runner image

Minimal smoke workflow that calls `command -v cc` and compiles a
trivial C program. Reproduces a cargo/Rust workflow failure where
`linker 'cc' not found` breaks builds under agent-ci-dev because
ghcr.io/actions/actions-runner:latest does not ship build-essential.

CI-only change (no packages/ code modified) — no changeset.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: pluggable runner image via .github/agent-ci.Dockerfile (#208)

agent-ci now discovers a user-provided Dockerfile at
.github/agent-ci.Dockerfile (or .github/agent-ci/Dockerfile for builds
with a COPY context), hashes its contents, builds it locally via
`docker build`, and uses the resulting agent-ci-runner:<hash> tag as
the default runner image. Edits trigger automatic rebuilds; identical
contents reuse the cached image.

This closes the gap where the minimal ghcr.io/actions/actions-runner
image lacks build-essential, python3, and other toolchains that
GitHub's hosted ubuntu-latest VM ships preinstalled. Workflows green
on GitHub but failing locally with `linker 'cc' not found` can now
opt into a richer image by dropping a 5-line Dockerfile into .github/.

Resolution order (highest wins):
  1. per-job `container:` (unchanged)
  2. AGENT_CI_RUNNER_IMAGE env var
  3. .github/agent-ci/Dockerfile (directory form, supports COPY)
  4. .github/agent-ci.Dockerfile (simple form, empty context)
  5. ghcr.io/actions/actions-runner:latest (default)

Also adds an error-hint heuristic: when a step fails with "command not
found" for cc/gcc/make/python3/pkg-config and the user is still on the
default image, the failure summary includes a paste-ready Dockerfile
pointing at the fix. Docs at packages/cli/runner-image.md.

Dogfoods the feature by shipping .github/agent-ci.Dockerfile for this
repo, which makes smoke-cc-compiler.yml (the issue #208 reproduction)
pass end-to-end.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: announce first-run runner image build

Print a one-time stderr notice before `docker build` fires so users
understand why the first run pauses for ~60s. The message lives in
stderr so log-update can't clobber it and stays in scrollback after.

Docs updated to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: clarify that container: runs verbatim, no Dockerfile layering

Spell out explicitly that a job-level container: directive bypasses
.github/agent-ci.Dockerfile entirely — agent-ci does not layer the
Dockerfile on top. This preserves the "same YAML runs locally and on
GitHub" parity promise.

Also document the two recommended ways to combine a custom base
image with agent-ci's extras.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 20:12:21 +02:00
Peter Pistorius 1a92bbd74d fix: preserve symlink path for Docker socket bind mount on macOS (#197) (#202)
* docs: simplify testing instructions in CLAUDE.md

Use single `npx agent-ci run --all` command instead of separate unit/e2e steps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: preserve symlink path for Docker socket bind mount on macOS (#197)

When /var/run/docker.sock is a symlink (Docker Desktop), the resolved
real path was used as the container bind-mount source, causing
"error while creating mount source path" in Docker's VM.

Adds `bindMountPath` to `DockerSocket` (the pre-symlink path) for bind
mounts, while `socketPath` (resolved) continues to be used for the
Docker API client. Adds a regression test for the specific failure case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: suppress passing step output in quiet mode

In --quiet mode, only emit failures and pause messages. Completed
and running step lines (✓/▸) and "Starting runner" headers were
noise on a passing run -- the final printSummary covers everything.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: add repro-197 integration test for Docker Desktop socket bind mount

Tests the full chain from filesystem state → socket resolution → container
bind string. Verifies that /var/run/docker.sock (the symlink) is used as
the bind-mount source, never the resolved ~/.docker/run/docker.sock that
causes Docker Desktop's VM to fail with "error while creating mount source path".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 13:41:23 +02:00
github-actions[bot] 33351243cd chore: version packages (#195)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.9.0 dtu-github-actions@0.9.0
2026-04-11 13:10:00 +02:00
Peter Pistorius 1e2714b101 fix: pull runner image before createContainer (#203) (#204)
* docs: simplify testing instructions in CLAUDE.md

Use single `npx agent-ci run --all` command instead of separate unit/e2e steps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: pull runner image before createContainer to fix first-run failure

Fixes #203

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 13:08:14 +02:00
dependabot[bot] e20712c39c chore(deps): bump the npm_and_yarn group across 3 directories with 7 updates (#200)
Bumps the npm_and_yarn group with 3 updates in the / directory: [react-server-dom-webpack](https://github.com/facebook/react/tree/HEAD/packages/react-server-dom-webpack), [rwsdk](https://github.com/redwoodjs/sdk/tree/HEAD/sdk) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).
Bumps the npm_and_yarn group with 3 updates in the /apps/website directory: [react-server-dom-webpack](https://github.com/facebook/react/tree/HEAD/packages/react-server-dom-webpack), [rwsdk](https://github.com/redwoodjs/sdk/tree/HEAD/sdk) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).
Bumps the npm_and_yarn group with 3 updates in the /experiments/pkg-cache-bench directory: [react-server-dom-webpack](https://github.com/facebook/react/tree/HEAD/packages/react-server-dom-webpack), [rwsdk](https://github.com/redwoodjs/sdk/tree/HEAD/sdk) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `react-server-dom-webpack` from 19.2.4 to 19.2.5
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react-server-dom-webpack)

Updates `rwsdk` from 1.0.4 to 1.0.6
- [Release notes](https://github.com/redwoodjs/sdk/releases)
- [Commits](https://github.com/redwoodjs/sdk/commits/v1.0.6/sdk)

Updates `vite` from 7.3.1 to 7.3.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.2/packages/vite)

Updates `basic-ftp` from 5.2.0 to 5.2.2
- [Release notes](https://github.com/patrickjuchli/basic-ftp/releases)
- [Changelog](https://github.com/patrickjuchli/basic-ftp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/patrickjuchli/basic-ftp/compare/v5.2.0...v5.2.2)

Updates `lodash` from 4.17.23 to 4.18.1
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `rollup` from 4.57.1 to 4.60.1
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.57.1...v4.60.1)

Updates `react-server-dom-webpack` from 19.2.4 to 19.2.5
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react-server-dom-webpack)

Updates `rwsdk` from 1.0.4 to 1.0.6
- [Release notes](https://github.com/redwoodjs/sdk/releases)
- [Commits](https://github.com/redwoodjs/sdk/commits/v1.0.6/sdk)

Updates `vite` from 7.3.1 to 7.3.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.2/packages/vite)

Updates `react-server-dom-webpack` from 19.2.4 to 19.2.5
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react-server-dom-webpack)

Updates `rwsdk` from 1.0.0-beta.57 to 1.0.6
- [Release notes](https://github.com/redwoodjs/sdk/releases)
- [Commits](https://github.com/redwoodjs/sdk/commits/v1.0.6/sdk)

Updates `vite` from 7.3.1 to 7.3.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.2/packages/vite)

---
updated-dependencies:
- dependency-name: react-server-dom-webpack
  dependency-version: 19.2.5
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: rwsdk
  dependency-version: 1.0.6
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: vite
  dependency-version: 7.3.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: basic-ftp
  dependency-version: 5.2.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: rollup
  dependency-version: 4.60.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: react-server-dom-webpack
  dependency-version: 19.2.5
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: rwsdk
  dependency-version: 1.0.6
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: vite
  dependency-version: 7.3.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: react-server-dom-webpack
  dependency-version: 19.2.5
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: rwsdk
  dependency-version: 1.0.6
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: vite
  dependency-version: 7.3.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-10 21:58:22 +02:00
Peter Pistorius b93ecdf303 feat: compute dirty SHA for uncommitted worktrees (#198) (#201)
When no --sha flag is given and the working tree is dirty, compute an
ephemeral commit SHA via git write-tree / commit-tree that represents
the actual code being executed. No refs are moved and history is
untouched.

Closes #198

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 19:52:32 +02:00
Peter Pistorius 68b1d14772 fix: show failure output for paused jobs in multi-job workflows (#196)
The last output lines and retry/abort hints were only displayed in
single-job mode. Multi-job workflows (e.g. build + test) would show
"Step failed attempt #1" with no error context or instructions.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 17:15:44 +02:00
Peter Pistorius 9ff07107ca fix: deduplicate identical failure errors in output summary (#194)
When multiple matrix jobs fail with the same error (e.g. missing secrets),
the FAILURES section now groups them and shows the error once instead of
repeating it for each job. Streaming error messages are also deduplicated.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 15:59:13 +02:00
Peter Pistorius 2cf4034821 feat: resolve workflow secrets from environment variables (#193)
* feat: resolve workflow secrets from shell environment variables

Shell env vars now act as a fallback for .env.agent-ci when resolving
${{ secrets.FOO }} references. Also auto-populates secrets.GITHUB_TOKEN
from --github-token.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add loadMachineSecrets env fallback tests, update README

Add 7 tests covering process.env fallback behavior in loadMachineSecrets:
file precedence, env-only resolution, key scoping, and merge behavior.
Add a Secrets section to the README documenting resolution order.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 15:54:38 +02:00
github-actions[bot] e958072dfe chore: version packages (#188)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.8.2 dtu-github-actions@0.8.2
2026-04-10 14:21:21 +02:00
Peter Pistorius e42f4a9efe fix: skip inaccessible docker sockets during resolution (#192)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 14:18:24 +02:00
Peter Pistorius cd24a04107 fix: support actions/checkout@v6 by using real HEAD SHA (#189)
Checkout v6 added post-fetch validation that the checked-out ref matches
github.sha. The git shim was returning a fake placeholder SHA (aaaa...)
in dirty workspace mode while github.sha used the real HEAD SHA, causing
the validation to fail. Use realHeadSha consistently everywhere and
remove the now-unnecessary computeFakeSha helper.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:13:42 +02:00
Peter Pistorius 02741dcf0a fix: mount warm node_modules directly at workspace path (#190)
The previous approach symlinked workspace/node_modules → /tmp/node_modules,
which broke esbuild path resolution in pnpm monorepos. Tools like rwsdk's
directive scanner resolved paths through the symlink incorrectly, producing
paths like apps/web/tmp/node_modules/.pnpm/... instead of /tmp/node_modules/.pnpm/...

Mount the warm cache as a Docker bind mount directly at the workspace
node_modules path. This makes the filesystem layout identical to production
GitHub Actions. The original EBUSY concern is already mitigated by the git
shim (blocks git clean) and checkout patching (clean: false).

Adds a smoke test exercising esbuild resolution through pnpm workspace
symlinks to prevent regression.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:13:16 +02:00
Peter Pistorius f7e42f0fb4 fix: clean up runner directory on SIGINT/SIGTERM (#187)
* fix: clean up runner directory on SIGINT/SIGTERM

The signal handler was missing `hostRunnerDir` from its cleanup list,
causing runner directories to leak when the process was interrupted
via Ctrl+C. Moved variable declarations above the handler so it can
reference `hostRunnerDir`, and added it to the cleanup loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: detect and kill orphaned containers via parent-PID liveness

Label every container with `agent-ci.pid=<PID>`. On startup, scan
running agent-ci-* containers and check if their parent PID is still
alive. Dead PID → orphan → kill it along with its sidecars and network.

Also wires up the existing but unused `pruneStaleWorkspaces()` to
remove run directories older than 24 hours.

Closes #186

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:09:01 +02:00
github-actions[bot] 7731f9cbdc chore: version packages (#185)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.8.1 dtu-github-actions@0.8.1
2026-04-10 11:58:18 +02:00
Peter Pistorius 1f24fecc14 fix: make GitHub auth opt-in for remote workflow fetching (#183)
* fix: make GitHub auth opt-in for remote reusable workflow fetching

Remove implicit credential resolution (gh auth token / GITHUB_TOKEN fallback)
that could silently send user credentials. Auth is now opt-in via
--github-token CLI flag or AGENT_CI_GITHUB_TOKEN env var.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: add changeset for opt-in GitHub auth

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add --github-token flag and AGENT_CI_GITHUB_TOKEN to docs

Update CLI reference tables, environment variables section, and
compatibility matrix to reflect opt-in GitHub auth for remote
reusable workflows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: make commit status posting opt-in via --github-token

Gate postCommitStatus() on the githubToken parameter. When no token
is provided, print a warning instead of silently using implicit
gh CLI credentials.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add --commit-status flag to opt in to posting GitHub commit statuses

Commit status posting now requires both --commit-status and
--github-token. Without --commit-status, no warning is printed
and no status is posted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add tests for 403 error hint and public repo fetch without auth

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 11:55:26 +02:00
Peter Pistorius d25354e3ca fix: disable ts-runner npm publishing (#182)
Mark @redwoodjs/ts-runner as private and add it to changeset ignore
list. The package was being published to npm but version 0.7.0 is
returning 404 errors, breaking installs.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 11:13:43 +02:00
github-actions[bot] b90191b477 chore: version packages (#181)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-10 10:53:40 +02:00
Peter Pistorius 2820b5a09f fix: remove test script from ts-runner to unblock release workflow (#180)
The ts-runner package had a "test": "vitest run" script but no test
files. Vitest exits with code 1 when it finds nothing to run, which
has been failing the Release workflow since the package was added,
preventing changesets/action from creating release PRs.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 10:49:00 +02:00
Peter Pistorius 912ed83465 fix: preserve git-tracked symlinks in workspace snapshot (#179)
* fix: mount warm-modules at /tmp/node_modules for pnpm TypeScript compat

TypeScript resolves @types/* by walking up from realpath(). With pnpm's
.pnpm virtual store symlinked to /tmp/warm-modules, the upward walk never
finds a node_modules directory. Mounting at /tmp/node_modules instead means
TypeScript's walk from /tmp/node_modules/.pnpm/... naturally finds
/tmp/node_modules/@types/.

Closes #176

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: preserve git-tracked symlinks in workspace snapshot

copyWorkspace was using cp/fs.cpSync which dereference symlinks, causing
git mode 120000 entries to be dropped from the workspace commit. Now we
check lstat() before copying and recreate symlinks via symlinkSync.

Closes #178

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add pnpm symlink smoke test reproducing #176 and #178

Adds a committed fixture at .github/fixtures/pnpm-symlinks/ with a
git-tracked symlink (mode 120000) and a TSX file that requires
@types/react resolution through pnpm's .pnpm virtual store.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 10:30:28 +02:00
Peter Pistorius 8fbe36d076 fix: mount warm-modules at /tmp/node_modules for pnpm TypeScript compat (#177)
TypeScript resolves @types/* by walking up from realpath(). With pnpm's
.pnpm virtual store symlinked to /tmp/warm-modules, the upward walk never
finds a node_modules directory. Mounting at /tmp/node_modules instead means
TypeScript's walk from /tmp/node_modules/.pnpm/... naturally finds
/tmp/node_modules/@types/.

Closes #176

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 08:40:23 +02:00
Peter Pistorius f660c11a4d feat: support composite action step outputs and changed-files actions (#174)
* feat: support composite action step outputs and push event context

Enable workflows that use composite actions with cross-step output
references (e.g. Khan/actions@get-changed-files + filter-files) by:

- Preserving steps.* expressions for runner-time evaluation instead
  of expanding them to empty string at parse time
- Using correct BasicExpression token type (type 3, not 6) matching
  the runner's wire format
- Adding ContextName to steps so the runner can resolve
  steps.<id>.outputs.<name> expressions
- Adding push event before/after SHAs and GITHUB_API_URL so
  changed-files actions can call the compare commits API
- Implementing compare commits and list-PRs-for-commit mock endpoints
  in the DTU server using git diff on the original repo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve symlinks in agent-ci-dev.sh so it works when invoked via symlink

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 23:02:21 +02:00
Peter Pistorius 789e403c74 feat: support reusable workflow inputs/outputs passing (#173)
Wire `with:` inputs from caller workflows to called workflows via
`inputs.*` context, resolve `on.workflow_call.inputs` defaults, and
propagate `on.workflow_call.outputs` back so downstream jobs can
access `needs.<callerJobId>.outputs.*`. Also fixes intra-workflow
`needs.*` resolution for composite job IDs.

Adds smoke test, unit tests, /validate command, and CLAUDE.md agent rules.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 21:08:59 +02:00
Peter Pistorius cf31ce145a feat: support nested reusable workflows up to 4 levels deep (#172)
Replace the nesting guard in expandReusableJobs with recursive expansion,
cycle detection, and a depth limit of 4 matching GitHub Actions' max.

Closes #167

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 20:13:29 +02:00
Peter Pistorius 6b7a95b180 feat: support local composite actions (#169)
* feat: support local composite actions (`uses: ./.github/actions/...`)

Set RepositoryType to "self" for local action references so the runner
resolves them from the workspace instead of trying to download a tarball.
Add defensive guard in actiondownloadinfo to skip local/empty actions.

Closes #162

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: fix formatting in .docs/ files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 17:07:31 +02:00
Peter Pistorius e9c5df5f6f feat: support reusable workflows (local and remote) (#163)
* feat: support local reusable workflows (uses: ./.github/workflows/...)

Inline called workflow jobs into the caller's dependency graph so that
`jobs.<id>.uses` with local refs works end-to-end. Includes dependency
rewiring, smoke tests, runner seed resilience fix, boot-failure log
surfacing, and dev script improvements.

Closes #149

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: update website compatibility matrix for reusable workflows

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add section descriptions to website compatibility matrix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add descriptions to compatibility matrix legend items

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add 'not planned' status icon to compatibility matrix

Distinguish architectural limitations (concurrency groups, GitHub
secrets) from features that could be implemented in the future.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: support remote reusable workflows (uses: owner/repo/path@ref)

Pre-fetch remote workflow YAML from GitHub Contents API before expansion,
cache SHA refs forever and always re-fetch mutable tag/branch refs. Also
changes missing workflow files from warn+skip to hard failure.

Closes #165

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: split monolithic smoke-tests.yml into discrete workflow files

Each smoke test area now has its own workflow file for isolated failure
visibility: matrix, artifacts/services, cross-job outputs, job conditions,
reusable workflows (local + remote), and binary smoke.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: fix formatting in .docs/ files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: clarify agent-ci-dev.sh is for local development only

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 17:07:13 +02:00
Patrick Gallagher d1d8452251 Fix tcp DOCKER_HOST handling in local runner (#161) 2026-04-09 13:08:49 +02:00
Peter Pistorius a99c81acbd Add agent-ci marketing and adoption strategy docs (#74)
* kindling: start task

* Add marketing strategy research and synthesis

- investigation-plan.md: scope and search strategy for both tracks
- research-developer-channels.md: how CI tools gain adoption (act case study, HN, Reddit, npm, messaging)
- research-agent-discoverability.md: how AI agents discover tools (llms.txt, CLAUDE.md, MCP, training data)
- marketing-strategy.md: synthesized multi-step plan for human devs and AI agents

* Fix three evidence quality issues flagged by Critic review

- Source the 844k llms.txt adoption figure to BuiltWith tracking dashboard
  (trends.builtwith.com/robots/LLMS-Text, as of Oct 25 2025); add caveat
  that other methodologies (NerdyData, SE Ranking) show far lower counts
- Reword LLMs-do-not-use-llms.txt claim from tested negative to absence
  of public documentation — more accurate epistemic framing
- Replace unsourced act tutorial claims with verified direct URLs: Red Hat,
  BrowserStack, Microsoft Azure, LogRocket, freeCodeCamp, plus 10 dev.to
  article links confirming the organic tutorial coverage

* Tighten sourcing and hedging language per Critic precision review

- HN traffic numbers: hedge 10k-100k as 'commonly estimated' with no single controlled measurement; add in-text Nebula Graph citation for 300+ stars figure
- Act HN appearances: reduce 'at least four times' to 'multiple times' (only two item IDs substantiated)
- Tutorial blog claim: 'all recommend act' softened to 'Leading tutorial blogs...recommend'
- Overnight agent commits: remove unsourced '1,000+' figure; soften to 'large batches of agent-generated commits'
- Reddit comment claim: reframe 'drives qualified signups for months' as 'can drive sustained qualified traffic'; note it reflects SEO best-practice inference, not a case study
- HN/Product Hunt 100 vs 30 comparison: add in-text citation bracket linking to Medium source

* Fix four citation issues flagged by Critic review

- Fix 1: Add inline WebProNews link to February 9, 2026 GitHub Actions outage claim (both files)
- Fix 2: Add HN item ID inline for Dagger stats (item?id=30857012)
- Fix 3: Replace 'typical' with 'commonly observed as' / 'reported in case studies'
- Fix 4: Generalize act fork/dependent stats with link to current repo instead of bare numbers

* Update strategy with Apr 7-9 tweet learnings and revised positioning

The Apr 7 "Running GitHub Actions locally" tweet hit 78K impressions —
revealing that the universal developer pain point is the primary hook,
not the AI agent angle. This updates the strategy doc with performance
data, a revised positioning funnel (hook → explain → differentiate →
convert), updated HN draft, and new action items. Adds tweet tracking
for the Apr 7-9 thread.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add tweet queue with 8 drafts following screenshot-first formula

Each tweet follows the pattern that drove 78K impressions on Apr 7:
one-line hook, terminal screenshot, no AI-agent lead. Covers dirty
tree, speed comparison, retry, git hooks, service containers, matrix
strategy, quiet mode, and act comparison. Includes posting order and
cadence recommendations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 10:16:07 +02:00
Peter Pistorius 7a612b041b fix: remove duplicate error messages on workflow failure (#160)
Remove the useless try/catch wrapper in handleWorkflow so errors are
only reported once by the top-level catch handler.

Closes #154

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 23:10:35 +02:00
Peter Pistorius 89d1483a7e feat: auto-detect Docker socket path when DOCKER_HOST is not set (#159)
* feat: auto-detect Docker socket path when DOCKER_HOST is not set

Resolves #158. Adds resolveDockerSocket() which finds the Docker daemon
socket by checking DOCKER_HOST, resolving symlinks at /var/run/docker.sock,
parsing `docker context inspect`, and falling back to well-known macOS
provider paths (OrbStack, Docker Desktop, Colima, Lima). Throws with
actionable guidance when no socket is found.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: lazy-init Docker socket resolution and client

Defer resolveDockerSocket() and Dockerode construction until first use
so that module import (e.g. --help) doesn't throw or shell out to
docker context inspect on machines without Docker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 23:00:57 +02:00
Peter Pistorius 81aedf33d6 fix: suppress stderr leak and support non-origin remotes (#157)
* fix: suppress stderr leak and support non-origin remotes

- Extract `getFirstRemoteUrl()` that prefers 'origin' but falls back to
  the first available remote, with stdio piped to suppress stderr
- Use `process.cwd()` instead of `PROJECT_ROOT` in `deriveGithubRepo()`
  so it detects the user's repo, not the CLI install location
- Share the helper between config.ts and cli.ts

Closes #152

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: handle trailing slash in remote URLs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add tests for parseRepoSlug and getFirstRemoteUrl

Extract regex parsing into exported parseRepoSlug() so both
config.ts and cli.ts share the same logic, and it's testable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: simplify getFirstRemoteUrl to single git remote -v call

One process spawn instead of up to three.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: extract resolveRepoSlug into config.ts with tests

Move repo detection out of cli.ts private function into a shared,
exported resolveRepoSlug() in config.ts so it's testable and
eliminates the duplicated logic between config.ts and cli.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: don't auto-detect repo at import time from process.cwd()

The config default is just a last-resort fallback. Real detection
happens via resolveRepoSlug(repoRoot) in cli.ts, anchored to the
workflow path — process.cwd() at import time was a guess that could
be wrong.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: throw when repo cannot be detected instead of using unknown/unknown

Silently defaulting to "unknown/unknown" would produce wrong API URLs,
container env vars, and directory names. Now resolveRepoSlug() throws
with a clear message pointing users to set GITHUB_REPO.

Also removes dead config.GITHUB_REPO fallbacks from runner code — the
repo slug is always resolved from the workflow path in cli.ts before
jobs are created.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: use git config instead of parsing git remote -v output

git config --get remote.<name>.url returns just the URL — no
tab-delimited line parsing needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: propagate workflow-derived repo slug back to config.GITHUB_REPO

Once resolveRepoSlug detects the repo from the workflow path, write
it back to config.GITHUB_REPO so downstream code (commit-status.ts,
runner fallbacks) sees the correct value without needing it passed
through explicitly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: env var GITHUB_REPO overrides auto-detection, not the reverse

GITHUB_REPO env var is the explicit override; auto-detection from
the workflow's git remote is the default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: verify GITHUB_REPO env var overrides auto-detection

Tests the priority contract: env var wins over git remote detection,
auto-detection is the fallback, and missing both throws.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: make githubRepo required in CreateRunDirectoriesOpts

It's always provided by cli.ts after resolveRepoSlug. Remove the
inconsistent defensive fallbacks ("unknown-repo" vs "repo") that
were dead code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use git remote get-url instead of git config --get

git config reads from the config hierarchy (local → global → system),
so on CI runners where actions/checkout configures git globally, it
finds the parent repo's remote.origin.url even inside a fresh git init
temp dir. git remote get-url is scoped to the repo's own remotes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 22:38:33 +02:00
Peter Pistorius c7d45a24a1 fix: use DOCKER_HOST socket path for container bind mount (#156)
* fix: use DOCKER_HOST socket path for container bind mount

The Docker socket bind mount was hardcoded to /var/run/docker.sock on the
host side, ignoring DOCKER_HOST overrides. Extract the resolved socket path
and pass it into buildContainerBinds so the host side matches the actual
daemon socket.

Closes #155

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: don't run dockerSocketPath through toHostPath

The socket path is already a host path (from DOCKER_HOST or the default),
so it should not be remapped by toHostPath which translates container
paths to host paths in nested Docker scenarios.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 21:57:53 +02:00
Peter Pistorius fbd8dea738 fix: enable horizontal scroll on code blocks in mobile in-app browsers (#146)
* fix: enable horizontal scroll on code blocks in mobile in-app browsers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use overflow-clip on Terminal and Panel components for mobile scroll

The outer overflow-hidden on these components creates a scroll container
that intercepts touch gestures in mobile WebViews, preventing the inner
overflow-x-auto from being scrollable. overflow-clip provides the same
visual clipping for rounded corners without creating a scroll container.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: make AGENTS.md terminal block full-width instead of half-grid

The AI Agent Integration section was in a 2-column grid with Quick Start,
constraining the AGENTS.md code block to half the page width. Now Quick
Start uses a 3-column grid for its short terminal blocks, and the
AGENTS.md block spans the full content width below.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: stack Quick Start terminals full-width instead of 3-column grid

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: stack Architecture Comparison as cards on mobile

Replace the horizontal table with stacked cards on mobile (md:hidden),
with the Agent CI row highlighted at the bottom of each card. Desktop
keeps the table layout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 17:45:50 +02:00
Peter Pistorius ed4e86c41c fix: guard against undefined template.jobs in parseWorkflowSteps (#148)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 13:02:36 +02:00
Peter Pistorius 1b1c664741 fix: guard against undefined template.jobs from workflow parser (#147)
When @actions/workflow-parser cannot model certain expressions (e.g.
runner.temp in env values), it may return a template without a jobs
array. This caused a TypeError crash. Adding nullish coalescing
fallbacks lets the existing "no jobs found" / "task not found" error
paths handle these cases gracefully.

Closes #96 (issue 1 of 3).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 13:00:10 +02:00
Peter Pistorius ba84b69b96 feat: add ts-runner package scaffold and research docs (#141)
Introduces the packages/ts-runner package skeleton with build config,
planning docs (PLAN.md, SPEC.md), and a research document exploring
RivetKit Agent OS integration. No runtime code yet — this sets up the
foundation for subsequent PRs.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 12:25:21 +02:00
github-actions[bot] 4de00d6948 chore: version packages (#122)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.7.1 dtu-github-actions@0.7.1
2026-04-07 15:43:50 +02:00
Peter Pistorius 8ff11e0497 test: add reproduction test for issue #126 (#132)
Adds a test that verifies resolveDtuHost() correctly uses the
AGENT_CI_DOCKER_BRIDGE_GATEWAY env var when the fs.existsSync mock
intercepts the /.dockerenv check. Verified passing both locally and
inside Docker via agent-ci.

Closes #126

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 15:22:41 +02:00
Peter Pistorius 73f6bf0685 fix: propagate job-level env into DTU Variables store (#104) (#131)
* fix: propagate job-level env into DTU Variables and add AGENT_CI_LOCAL to container env

Closes #104. Job-level `env` from the CLI payload was not propagated into
the DTU's Variables store or ContextData, so `AGENT_CI_LOCAL` was unavailable
to the runner's expression engine. Also adds `AGENT_CI_LOCAL=true` to the
Docker container env for shell access.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: re-trigger agent-ci checks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 13:44:47 +02:00