* feat(compat): make compatibility.json the source of truth
Flip the compatibility matrix so packages/cli/compatibility.json is
authoritative. scripts/gen-compatibility-md.mjs renders the .md from it,
pnpm check runs a drift check, and the website imports the JSON directly
and renders inline markdown in keys/notes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test: add compat smoke coverage for secrets, working-dir defaults, runner image
Fill three audit gaps identified against packages/cli/compatibility.json:
- smoke-secrets-env-file: secrets resolved from .env.agent-ci and from
the shell environment fallback
- smoke-defaults-workdir: workflow- and job-level
defaults.run.working-directory plus a step-level override
- smoke-runner-image: .github/agent-ci.Dockerfile is picked up and the
resulting image is used at job start
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(workflow): propagate defaults.run.working-directory to steps
Parse defaults.run.working-directory at workflow and job level and merge
it into each run-step's inputs with step > job > workflow precedence.
Step-level working-directory already worked; the defaults blocks were
silently ignored.
Closes#290.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(compat): add proof field to compatibility.json rows
Every `supported`/`partial` row now lists the smoke workflows that
exercise it end-to-end. `pnpm compat:gen`/`compat:check` fails if a
listed path does not resolve, catching renames.
Rows with an empty `proof: []` are the audit gaps tracked in #292.
Refs #292.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(dtu): attach step env per-step so it does not leak to siblings
Step-level env was being aggregated into the job-wide EnvironmentVariables
map (last-wins), so a later step's `env: FOO=x` would override an earlier
step's effective FOO at runtime. Emit step.Env as the mapped step's own
`environment` instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(compat): fill audit gaps #292 — env, expressions, local action
Three smokes covering eight of the ten gaps listed in #292:
- smoke-env-scoping: workflow + job + step `env:` precedence
- smoke-expressions: hashFiles, format, join, runner.arch, type
literals, startsWith/endsWith
- smoke-local-action: `steps[*].uses: ./.github/actions/...`
(verified via marker file rather than steps.<id>.outputs.*)
Proof fields updated for every row these smokes cover. hashFiles
downgraded to ⚠️ partial — agent-ci emits SHA-1, GitHub Actions emits
SHA-256 (tracked by #294).
Two gaps remain open as filed issues:
- #293 defaults.run.shell / steps[*].shell not applied
- #295 ${{ steps.<id>.outputs.<name> }} leaks literal text in run
scripts
Refs #292.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#266 merged an edit to home.md that didn't match oxfmt's canonical
markdown table formatting, leaving `pnpm format:check` (and therefore
`pnpm check`) red on main. Run `pnpm exec oxfmt` to repair.
No changeset: apps/website is private.
Implements the 'Markdown for Agents' agent-readiness goal for '/' without
relying on any Cloudflare dashboard feature flag.
- apps/website/src/app/pages/home.md — hand-authored markdown version of
the homepage. Same content as home.tsx (hero, principles, architecture
comparison, quick start, agent instructions, testimonials) but without
the React-only decorative bits (CRT effects, sticky nav, avatars).
Lives next to home.tsx so they stay in sync through review.
- apps/website/src/app/lib/markdown-negotiation.ts — Accept-header parser
and route middleware factory. Returns markdown only when the client
explicitly lists 'text/markdown' and its q-value beats text/html
(RFC 9110 §12.5.1). Sets Vary: Accept so caches key correctly. Bare
'*/*' is treated as browser default -> HTML, matching Cloudflare's
'Markdown for Agents' semantics.
- apps/website/src/worker.tsx — wire the middleware into route('/') via
the array form: [serveMarkdownIfPreferred(homeMarkdown), Home]. The
React component renders normally unless the middleware short-circuits.
- apps/website/types/vite.d.ts — declare *?raw module so the markdown
import type-checks.
Verified against 'npm run preview':
Accept: text/html, ... */*;q=0.8 -> text/html
Accept: text/markdown -> text/markdown; charset=utf-8
Accept: text/markdown;q=0.9, ... q=.8 -> text/markdown
Accept: text/html, text/markdown;q=.5 -> text/html
Accept: */* -> text/html
No changeset: change is entirely under apps/website/; does not touch packages/.
Address agent-readiness findings from isitagentready.com:
- apps/website/public/robots.txt — RFC 9309 crawl rules with explicit
entries for GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-Web,
anthropic-ai, Google-Extended, PerplexityBot, Applebot-Extended, CCBot,
Meta-ExternalAgent, Bytespider, DuckAssistBot (and standard search bots),
a Sitemap reference, and Content Signals
(search=yes, ai-input=yes, ai-train=no).
- apps/website/src/app/pages/sitemap.ts — dynamic /sitemap.xml handler
registered outside render(Document, ...) so it returns application/xml.
Enumerates static routes, the copied CLI docs, and blog posts from
content-collections with lastmod.
- apps/website/src/app/headers.ts — adds a RFC 8288 Link response header
on all routes: sitemap, agent-skills (extension), service-doc.
- apps/website/scripts/copy-docs.mjs — sync script that copies the public
CLI docs (README.md, compatibility.md, runner-image.md, SKILL.md) into
apps/website/public/docs/ and regenerates
apps/website/public/.well-known/agent-skills/index.json with fresh
sha256 digests. Hooked into predev/prebuild so the website's static
tree cannot drift from the CLI.
The generated public/docs/ and public/.well-known/agent-skills/ trees are
gitignored.
Verified end-to-end against 'npm run preview': all endpoints return the
expected status, content-type, and body, and the Link header appears on
both HTML and XML responses.
No changeset: this change is entirely under apps/website/ and does not
affect published packages (per .claude/rules/changeset-required.md).
* fix: skip orphan cleanup when running inside a Docker container
When agent-ci runs nested inside a container (e.g. smoke-bun-setup step 8),
it shares the host's Docker socket. killOrphanedContainers() checks
agent-ci.pid labels using process.kill(pid, 0), but host PIDs don't
exist in the container's PID namespace — so every container looks like
an orphan. This caused the nested agent-ci to force-remove its own
parent container (exit 137).
Both pruneOrphanedDockerResources() and killOrphanedContainers() now
bail out when /.dockerenv exists.
Fixes#228
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: ignore inherited AGENT_CI_DTU_HOST inside containers
When agent-ci runs nested inside a container (e.g. smoke-bun-setup),
the outer container's AGENT_CI_DTU_HOST=host.docker.internal is
inherited. The nested agent-ci used this to tell the inner container
where the DTU is, but the nested DTU is inside the outer container,
not on the host. The inner container got "Connection refused" trying
to reach host.docker.internal:PORT.
Now when /.dockerenv exists, AGENT_CI_DTU_HOST is ignored and the
container's own IP (via hostname -I) is used instead, so sibling
containers can reach the DTU.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: move /.dockerenv guard to call site so tests still work
The /.dockerenv check inside killOrphanedContainers() broke the
shutdown integration tests — they run inside a Docker container (the
test runner) and need the function to work. Moved the guard to the
session bootstrap in cli.ts where the nested agent-ci problem occurs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add global concurrency limiter across all workflows
In --all mode, each workflow created its own per-wave limiter, allowing
20+ containers to run simultaneously and exhaust available memory (OOM
kills with exit 137). Now a single global limiter (CPU/2) is created in
the orchestrator and shared by all handleWorkflow calls, including
single-job workflows and cold-cache first jobs.
Closes#225
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: factor Docker VM memory into concurrency limit
CPU-based limit alone (cpuCount/2 = 7) still causes OOM kills. Now
reads MemAvailable from /proc/meminfo inside the Docker VM and caps
at floor(available / 3GB per container). Falls back to docker info
with a 4GB reserve if busybox isn't available.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: guard orphan cleanup in the function, not just the call site
The /.dockerenv guard was only in cli.ts, but killOrphanedContainers
is also called directly by the Docker integration tests (via a spawned
tsx subprocess). Inside a container, the test's subprocess would see
the parent container's PID label, misidentify it as an orphan, and
kill the container running the tests.
Put the guard back in both functions AND keep the cli.ts guard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: increase per-container memory estimate to 4 GB
3 GB per container wasn't enough — tests.yml (vitest + Docker
integration tests) and smoke-binary.yml (full build) both got killed
at 3 concurrent containers. 4 GB gives 2 concurrent containers on a
12 GB Docker VM, with ~5.4 GB each.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: mock fs.existsSync in shutdown unit tests for /.dockerenv guard
The unit tests mock execSync but not fs. Inside a runner container,
the real fs.existsSync("/.dockerenv") returns true, causing
killOrphanedContainers to bail before reaching the mocked execSync.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: skip Docker integration tests for orphan cleanup inside containers
The integration tests spawn a real killOrphanedContainers() subprocess.
Inside a container, the function bails due to /.dockerenv (PID namespace
mismatch), so the tests can't exercise it. Skip them with insideDocker().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add --jobs flag and document concurrency
Adds -j/--jobs <n> to override the auto-detected concurrency limit.
Adds a Concurrency section to the README explaining how the limit is
calculated and how to override it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(website): include install steps in copy agent instructions
The "Copy agent instructions" button and Terminal preview now include
setup steps: installing the package (with package manager detection for
Node.js projects, or npx for non-Node projects) and installing the
agent skill.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: unify agent instructions across repo, README, and website
Remove the .agents/ hook system and Machinen-specific CLAUDE.md instructions.
Public-facing docs (README, website) now show generic commands that work with
any agent harness. CLAUDE.md uses the /validate skill directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: mention /validate skill on website
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add Agent Skills spec skill and update docs with `npx skills add`
Ship an agentskills.io-compatible skill at skills/agent-ci/SKILL.md so
users can install with `npx skills add redwoodjs/agent-ci --skill agent-ci`.
Works with Claude Code, Cursor, Codex, and 40+ other agents.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add penberg testimonial, update bebraw quote
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: simplify agent instructions to reference the skill
Instead of repeating raw commands, tell agents to run the `agent-ci` skill
that was installed via `npx skills add`.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add AI disclosure to blog posts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: move AI disclosure to top of blog posts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: clarify runner image vs VM image in blog post
Explain that agent-ci uses the ~400 MB official runner binary, not
GitHub's ~30 GB VM image.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: show nav links on mobile across all pages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: wrap nav links to second row on mobile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: show Star us button on mobile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 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>
* 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>
* feat(website): add blog section with first post
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(website): integrate blog section with routing and styles
* Add lock file.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(website): add Copy Agent Instructions button to hero and quick start sections
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: remove retry-proof failure test from CI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- CompatibilityMatrix: keep individual tables rendering, take expanded notes from main
- headers.ts: keep both pbs.twimg.com and unavatar.io in img-src CSP
- wrangler.jsonc: keep agent-ci-website worker name and routes
- compatibility.md: keep expanded notes from feat/website
- Remove welcome.tsx and welcome.module.css (replaced by new site)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move website from website/ to apps/website/ (update pnpm workspace)
- Remove logo icon from navbar, keep AGENT-CI text
- Configure domain routes for agent-ci.dev in wrangler.jsonc
- Replace tabs with stacked tables in compatibility matrix
- Expand all ⚠️ and ❌ notes with reasons and details
- Update compatibility.md to match website content
- Fix useContext SSR error by removing motion/react, using CSS animations
- Add img-src CSP header for pbs.twimg.com (Twitter profile images)
- Remove npx run --all command from hero, rename button to Quick start
- Remove decorative border elements from hero section
- Update footer "Built by" link to rwsdk.com
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>