* fix(cli): unblock --pause-on-failure in non-TTY pipes (#315)
Spawn the run as a detached worker when stdout isn't a TTY (and we're
not under an LLM harness), then exit 77 the moment the worker emits a
`run.paused` NDJSON event — freeing the caller's pipe while the worker
keeps the container + DTU alive for `agent-ci retry`. Retry tails the
same log so a re-failure surfaces as another exit-77.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(cli): NDJSON event stream behind --json (#289)
Extends the existing pause/finish sentinel emitter into a full lifecycle
event stream. New `--json` flag (and `AGENT_CI_JSON=1` env var) emits
`run.start`, `job.start`/`finish`, `step.start`/`finish`, `run.paused`,
`run.finish`, `diagnostic` events on stdout — one JSON object per line —
each carrying an `event` discriminator and a `schemaVersion: 1` on
`run.start`.
`--json` is decoupled from `--quiet` so existing `-q` callers keep silent
stdout; the diff renderer is auto-suppressed under `--json` so ANSI
sequences don't collide with the stream. Detached-worker mode (#315)
keeps emitting only sentinels — the launcher filters non-sentinel events
anyway, so non-JSON callers don't pay the cost.
Renames `run.completed` -> `run.finish` to match the issue's naming.
Closes#289.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(cli): suppress printSummary under --json
The ━━━ FAILURES ━━━ / ━━━ SUMMARY ━━━ blocks write to stdout
unconditionally, which interleaves with the NDJSON event stream and
breaks line-based parsers. Skip them when isJsonMode() — consumers get
the same information from job.finish + run.finish events.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(cli): AGENT_CI_DETACHED=1 forces detached launcher on TTY
Overload AGENT_CI_DETACHED so it can serve both roles:
- `=<absolute path>` → set by the launcher on its worker child;
`isDetachedWorker()` returns true.
- `=1` (or any non-absolute value) → caller-side opt-in: force the
launcher path even when stdout is a TTY. Useful for manual
verification of the #315 pause-then-exit-77 flow without having to
construct a non-TTY pipe.
The two are distinguished by whether the value starts with `/`. New
`isForceDetachedRequested()` helper drives a `forceDetached` arg into
`shouldLaunchDetached`, which still respects `alreadyWorker`,
`agentMode`, and the `pauseOnFailure` requirement.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(ci): smoke for --json NDJSON event stream (#289)
scripts/smoke-289.sh creates a tmp repo with a 2-step passing workflow,
runs `agent-ci run --json`, and asserts:
- run.start carries schemaVersion=1
- step.start + step.finish for each declared step
- job.finish status=passed
- run.finish status=passed
- every JSON-shaped line on stdout has a recognized event field
.github/workflows/smoke-json-events.yml runs the script in CI on every
PR (gated by the same safe-to-run / collaborator predicate as the other
smokes).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): stop smoke-resource-mismatch from hanging on real GitHub runners
The fixture in .github/workflows/smoke-resource-mismatch.yml declares
`runs-on: ubuntu-latest-999-cores` so agent-ci's resource-fidelity
classifier deterministically tags the job as `degraded` on any host.
With its previous `pull_request_target` trigger, GitHub Actions also
picked it up on every PR and queued a job that no real runner can
satisfy — five queued runs piled up on PR #323 alone, each timing out
after hours.
Switch the workflow to `workflow_dispatch:`-only so GitHub never
auto-dispatches it, and teach `isWorkflowRelevant` to treat
`workflow_dispatch:`-only workflows as relevant for `agent-ci run --all`
so the local smoke keeps running. The opt-in is gated to the sole-event
case to avoid overriding `pull_request` / `push` path/branch filters on
workflows that legitimately combine triggers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes#297. CI/scripts-only — no packages/ change, so no changeset required
per .claude/rules/changeset-required.md.
- scripts/diff-parse.ts: walks raw YAML at workflow + job scopes (in addition
to the existing step scope) and grades every recognized key against a
preservation predicate or a documented expected-drop. Reuses focused parsers
(parseJobIf, parseJobOutputDefs, parseWorkflowContainer, parseWorkflowServices,
parseMatrixDef, parseFailFast, parseJobDependencies, getWorkflowTemplate).
defaults.run.shell preservation detects the heredoc-wrap marker emitted by
wrapScriptForShell.
- third-party-workflows/: vendored scan-only corpus — turborepo-lint.yml,
playwright-infra.yml, docker-validate.yml — each with attribution header.
Picked up automatically by collectWorkflowFiles.
- package.json: parse:diff added to the parallel pnpm check pipeline so
regressions fail standard CI.
Result: 37 workflows scanned (.github/workflows + third-party-workflows),
0 drifts. pnpm check exits 0; pnpm agent-ci-dev run --all reports 39/39.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
For each .github/workflows/*.yml, read the raw YAML and compare against
what parseWorkflowSteps emits, flagging any step-level key our parser
silently drops without a documented expected-drop policy.
First run over all 26 workflows: 0 drifts. Job-level and workflow-level
scans are tracked as follow-ups in #297.
Refs #297.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 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>
During pnpm run version, scripts/release-closes.mjs captures
Closes|Fixes|Resolves #N references from pending changesets into
.release-closes.json, pairs each with the PR that added the
changeset, and rewrites the keywords to Refs #N so the 'Version
Packages' PR does not close them on merge. After the release
workflow publishes, a new step closes each captured issue with
the comment 'Closes Issue #N via PR #M.'
* 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>
* 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>
- Rename package from 'machinen' to 'agent-ci' across all source, tests, scripts, configs, and docs
- Rename CLI binary from 'machinen' to 'agent-ci'
- Rename .machinen → .agent-ci, .env.machinen → .env.agent-ci, MACHINEN_* → AGENT_CI_*
- Rename --runner flag to --name/-n (--runner still accepted for compat)
- agent-ci-dev script now builds DTU before running CLI
- Extract YAML compatibility tables to cli/compatibility.md
- Expand CLI usage docs with per-command flag tables
- Document DOCKER_HOST support for remote Docker