The deploy step refuses a documentation-only merge, and the ship tick stalls 45 minutes on it #219

Closed
opened 2026-08-30 08:23:25 +00:00 by viberfox-agent · 5 comments
Collaborator

Problem

.forgejo/workflows/ci.yml:64-74 ignores docs/** and **/*.md on both the push: [main] and pull_request triggers, so a documentation-only merge commit on main gets no Actions run at all. Two consumers of that fact disagree.

tools/autopilot.ts knows the rule. When jobsFor(sha) comes back empty, shipReady re-reads the branch diff and merges without a verdict if every changed file is docs (tools/autopilot.ts:279-292, the predicate itself at :287). A third prose copy of the same rule sits in mainVerdict's doc comment at tools/autopilot.ts:421-424.

tools/deploy-main does not. shipReady hands main to it unconditionally at tools/autopilot.ts:309 → deployMain at :389, which runs the script bare (:392), so deploy-main resolves the sha as the head of main itself (tools/deploy-main:121-122) and enters the wait loop at tools/deploy-main:135-152. That loop only leaves when at least one job exists and none is pending (:141); with no job ever, it spins at 20-second intervals until timeout_min (default 40, tools/deploy-main:44) and exits 1 with refusing: no Actions job ran (:144).

Both halves are individually right. deploy-main's gate is the one docs/direction.md:115-119 calls load-bearing — "a silent runner failure publishes untested code" — and the issue is explicit that it must not be weakened. What is wrong is that the lane spends the whole timeout discovering something it already computed, and then posts the failure text at tools/autopilot.ts:411 ("Merged, but not deployed … the public services are still on the previous build") for a commit that had nothing to publish.

The merge commits named in the issue are docs-only in the API's own view: 67c0c14 reports one changed file, docs/notes/road-junction-geometry.md (GET /api/v1/repos/jeroen/cartopolis/git/commits/67c0c14, files[]).

Approach

Take option 2 from the issue — the autopilot does not call the deploy — and factor the rule so it is read from ci.yml instead of hardcoded. One file, tools/autopilot.ts, plus documentation.

Recommending option 2 over option 1 for one reason worth stating: deploy-main is the gate whose only job is to refuse, and teaching it a new way to succeed — a YAML parse plus glob matching, in bash/curl/python with no YAML library — puts new code in the most dangerous place in this lane. shipReady has already made the more consequential decision (merge with no verdict) from the same fact; adding "and therefore there is nothing to deploy" to a decision already taken adds no new authority. deploy-main is not touched, so its refusal behaviour is bit-identical.

  1. One rule, read from ci.yml on main. Add a helper beside frontier() (which already fetches a file from main over the API at tools/autopilot.ts:501 and is the pattern to copy) that fetches .forgejo/workflows/ci.yml?ref=main, extracts the paths-ignore patterns, and answers "is every one of these files ignored by CI?".
    • Extraction without a YAML dependency: scan for every paths-ignore: key and take the - '…' items indented under it. Require at least one block and require all blocks to have identical contents; if they differ (someone diverged the push filter from the pull_request one), that is not a rule this helper can state.
    • Pattern translation, a whitelist of the two shapes actually in use: <prefix>/** → the file starts with <prefix>/; **/*.<ext> → the file ends with .<ext>. Any other pattern shape is unrecognised.
    • Fail closed, every time. Unrecognised pattern, fetch failure, parse failure, no block found, blocks disagreeing, or an empty changed-file list → answer "not ignorable", log why, and fall through to the existing behaviour (wait for CI, which for a docs-only branch means the ticket sits until the next tick and is retried). Never "no job found means fine" — that is the hole docs/direction.md:115 exists to close.
  2. Use it in place of the hardcoded predicate at tools/autopilot.ts:287, and carry its answer forward: hold the result in a local alongside merged (:250), and at :309 either call deployMain(n) as now, or — when the merge was docs-only — post one comment saying documentation only, nothing to deploy, and skip the call. The label handling at :305-306 and the merge comment at :307 are unchanged. Memoise the ci.yml fetch for the duration of a shipReady pass; it loops over up to 20 issues.
  3. Make it checkable without a live ticket. There is no test runner for tools/*.ts (no package.json, no tsconfig.json, no CI job runs bun over tools/), and status does not exercise shipReady (tools/autopilot.ts:1087). Add a read-only subcommand — bun tools/autopilot.ts paths <branch-or-sha> — that prints the patterns it parsed out of ci.yml, the changed files it resolved, and the verdict, and exits. It writes nothing and posts nothing. This is the only way an implementer can demonstrate the rule is right before it decides a real deploy.
  4. Documentation. docs/workflow.md:137 says the lane runs tools/deploy-main after a merge; amend it to say that a documentation-only merge skips it. Record the standing fact in docs/notes/ci-build-cost.md (which already owns paths-ignore at :86): the filter now has readers outside the workflow file, so a pattern added there is a pattern autopilot.ts must be able to translate, and one it cannot translate stops the lane rather than deploying blind. Fix the stale prose copy of the rule in the mainVerdict comment at tools/autopilot.ts:421-424 to point at the helper rather than restating docs/** and *.md.

Note that tools/autopilot.ts is inside the autopilot's own fence (FENCED, tools/autopilot.ts:212) and .forgejo/workflows/ is too (:211), so this branch keeps its branch and loses ship by design. It lands for review. Do not apply the ship label.

Acceptance criteria

  • The strings docs/ and .md no longer appear as a path rule anywhere in tools/autopilot.ts — the only source of the patterns is .forgejo/workflows/ci.yml on main, fetched at run time.
  • tools/deploy-main is unmodified. git diff main -- tools/deploy-main is empty.
  • shipReady's existing docs-only merge path (tools/autopilot.ts:279-292) behaves as before for a branch whose files are all under docs/ or end in .md: it merges without a verdict.
  • After a docs-only merge, deployMain is not called, and exactly one comment is posted in its place. It says the change is documentation only and there is nothing to deploy, and it does not claim a failure or that "the public services are still on the previous build".
  • After a merge that is not docs-only, deployMain(n) is called exactly as it is today, with no new arguments and no change to tools/autopilot.ts:389-415.
  • Each of these makes the helper answer "not ignorable" (verified through the new paths subcommand, one run per case): ci.yml cannot be fetched; the file has no paths-ignore block; the two blocks in it differ; a pattern is present that is neither <prefix>/** nor **/*.<ext>; the changed-file list is empty. Each logs which of those it was.
  • bun tools/autopilot.ts paths 67c0c14 reports the two patterns from ci.yml, the one changed file docs/notes/road-junction-geometry.md, and a verdict of "ignored by CI". It posts no comment and changes no label.
  • bun tools/autopilot.ts paths ab430c5 (one changed file, tools/autopilot.ts) reports "not ignored".
  • bun tools/autopilot.ts status still runs and prints the same shape of JSON.
  • docs/workflow.md:137's paragraph states the docs-only skip.
  • docs/notes/ci-build-cost.md records that paths-ignore has a reader outside the workflow file, and that an unrecognised pattern stops the lane rather than deploying blind.
  • The mainVerdict comment at tools/autopilot.ts:421-424 no longer restates the rule in prose.
  • The branch is left with agent:done and without ship.

Verification

Read-only, no Rust involved; nothing here needs a GPU, a shot or a workstation.

# type/parse check — there is no test runner for tools/, so this is the floor
bun --print 'null' >/dev/null            # confirms bun is present
bun build --target=bun tools/autopilot.ts --outfile=/dev/null

# the rule, against real commits (needs FORGEJO_TOKEN in the environment;
# both of these are public commits and the call is a GET)
bun tools/autopilot.ts paths 67c0c14     # docs-only merge  → ignored by CI
bun tools/autopilot.ts paths ab430c5     # tools/ merge     → not ignored
bun tools/autopilot.ts paths 20b036a     # mixed merge      → not ignored

# the fail-closed arms: point the helper at a doctored ci.yml
# (however the implementer chooses to inject one — an env override on the
# fetch URL, or a fixture path — the five cases above must each be shown)

bun tools/autopilot.ts status            # unchanged output shape

Not checkable here, and it is what the issue says success is: the next documentation-only ticket the lane ships posts no failure comment and does not stall. That can only be seen in the lane's own comments after the change is copied into the running container.

The deployed copy is a manual step. docs/workflow.md:854 records that /data/agent/autopilot/autopilot.ts is a copy of tools/autopilot.ts; the mount is read-only and the container picks up a new file on its next tick. Merging this branch changes nothing until that copy is made, so the fix does not take effect on merge alone. Say so when handing the branch over.

Out of scope

  • Changing tools/deploy-main at all, including its message. A human running it by hand right after a documentation-only merge will still wait --timeout-min and get refusing: no Actions job ran. That residual is deliberate: it is the untouched gate, it costs a person a Ctrl-C rather than costing the lane 45 minutes six times a day, and closing it means putting the path rule inside the refusal gate, which the issue and docs/direction.md:115-119 both warn against.
  • Lowering timeout_min, or any other change to how long the deploy waits.
  • Making deploy-main accept a --sha, or having the autopilot pass one.
  • Anything about the FENCED table, the ship label, or the lane's other gates.
  • Adding a test runner, package.json or tsconfig.json for tools/.
  • The .forgejo/workflows/ci.yml filter itself — no pattern is added, removed or reworded.

Open questions

None.


Branch: fix/219-autopilot-docs-only-deploy

Original request

Found by the 3-day retrospective on #218, reading the lane's own comments for 2026-08-29 and 2026-08-30.

What happens

Six tickets in the period ended with this comment, minutes-to-an-hour after a clean merge:

🤖 Merged, but not deployed. The deploy step exited 1:

main is at 101525c2
  waiting for CI on 101525c2
refusing: no Actions job ran for 101525c2 — nothing has verified this commit

#186, #192, #176, #197, #199 and #201. Every one of them is a documentation-only change — a docs/notes/ file and a row in docs/direction.md recording that a source was checked and not taken. There was nothing to deploy and nothing to verify.

Why it happens

.forgejo/workflows/ci.yml has a paths-ignore rule, and states its reason at length: a Markdown change should not compile Bevy. So a docs-only merge commit on main correctly gets no Actions job at all.

tools/autopilot.ts already knows this. shipReady compares the branch against main, sees that every changed file is under docs/ or ends in .md, logs "documentation only — CI does not run on these, shipping" and merges without a verdict. That is right.

tools/deploy-main does not know it. Its gate (around line 130) loops until a job appears, and when none ever does it exits 1 with refusing: no Actions job ran. That is also right in isolation — it is the load-bearing second check docs/direction.md describes, the one that stops a timed-out CI wait from reaching the deploy, and it must not be weakened. The two halves simply disagree about what a docs-only commit is.

What it costs

Two things, and the second is the one that matters.

  • A false alarm on six tickets. "Merged, but not deployed … the public services are still on the previous build" reads as a failed deploy. Nothing failed; there was nothing to publish. A message that cries wolf six times in a day is one nobody reads the seventh time — and the seventh will be a real refusal.
  • The ship tick blocks for the whole timeout. deployMain is awaited inside shipReady's loop, and the wait runs to timeout_min before refusing. #186 merged at 20:51 and the refusal landed at 21:35; #176 merged at 21:52 and refused at 22:36. That is ~45 minutes of the lane sitting still, per docs-only ticket, six times in this period.

Where the seam is

  • tools/deploy-main — the wait loop and refusing: no Actions job ran
  • tools/autopilot.ts — shipReady's docs-only branch (the rule already exists there), and deployMain
  • .forgejo/workflows/ci.yml — the paths-ignore block the rule has to agree with

What would settle it

Either half works; whichever is chosen, the two must read the same rule from one place rather than each carrying its own copy of "docs/ or *.md", because the next path added to paths-ignore will otherwise re-open this.

  1. deploy-main learns the rule. Compare the commit's changed files against paths-ignore; if every one is ignored, say "nothing to deploy: documentation only" and exit 0. It must stay a whitelist of ignored paths, never "no job found means fine" — that is exactly the hole the gate exists to close.
  2. Or the autopilot does not call it. shipReady has already computed the file list by the time it merges; when the merge was docs-only, skip deployMain and comment "documentation only — nothing to deploy".

Success is judged from the lane's own comments, not from a picture: the next docs-only ticket merges and posts no failure, and the ship tick does not stall for the timeout.

A note on labels

Not ship. tools/autopilot.ts and tools/deploy-main are both inside the autopilot's own fence (autopilot.ts, the FENCED table), so a branch touching them keeps its branch and loses ship by design. It lands for review, which is the point.

Filed by the retrospective on #218.

🤖 Refined by the viberfox issue agent. Reply with @agent refine and what is wrong to have this rewritten.

## Problem `.forgejo/workflows/ci.yml:64-74` ignores `docs/**` and `**/*.md` on both the `push: [main]` and `pull_request` triggers, so a documentation-only merge commit on `main` gets no Actions run at all. Two consumers of that fact disagree. `tools/autopilot.ts` knows the rule. When `jobsFor(sha)` comes back empty, `shipReady` re-reads the branch diff and merges without a verdict if every changed file is docs (`tools/autopilot.ts:279-292`, the predicate itself at `:287`). A third prose copy of the same rule sits in `mainVerdict`'s doc comment at `tools/autopilot.ts:421-424`. `tools/deploy-main` does not. `shipReady` hands `main` to it unconditionally at `tools/autopilot.ts:309` → `deployMain` at `:389`, which runs the script bare (`:392`), so `deploy-main` resolves the sha as the head of `main` itself (`tools/deploy-main:121-122`) and enters the wait loop at `tools/deploy-main:135-152`. That loop only leaves when at least one job exists and none is pending (`:141`); with no job ever, it spins at 20-second intervals until `timeout_min` (default 40, `tools/deploy-main:44`) and exits 1 with `refusing: no Actions job ran` (`:144`). Both halves are individually right. `deploy-main`'s gate is the one `docs/direction.md:115-119` calls load-bearing — "a silent runner failure publishes untested code" — and the issue is explicit that it must not be weakened. What is wrong is that the lane spends the whole timeout discovering something it already computed, and then posts the failure text at `tools/autopilot.ts:411` ("Merged, but not deployed … the public services are still on the previous build") for a commit that had nothing to publish. The merge commits named in the issue are docs-only in the API's own view: `67c0c14` reports one changed file, `docs/notes/road-junction-geometry.md` (`GET /api/v1/repos/jeroen/cartopolis/git/commits/67c0c14`, `files[]`). ## Approach Take option 2 from the issue — the autopilot does not call the deploy — and factor the rule so it is read from `ci.yml` instead of hardcoded. One file, `tools/autopilot.ts`, plus documentation. Recommending option 2 over option 1 for one reason worth stating: `deploy-main` is the gate whose only job is to refuse, and teaching it a new way to *succeed* — a YAML parse plus glob matching, in bash/curl/python with no YAML library — puts new code in the most dangerous place in this lane. `shipReady` has already made the more consequential decision (merge with no verdict) from the same fact; adding "and therefore there is nothing to deploy" to a decision already taken adds no new authority. `deploy-main` is not touched, so its refusal behaviour is bit-identical. 1. **One rule, read from `ci.yml` on `main`.** Add a helper beside `frontier()` (which already fetches a file from `main` over the API at `tools/autopilot.ts:501` and is the pattern to copy) that fetches `.forgejo/workflows/ci.yml?ref=main`, extracts the `paths-ignore` patterns, and answers "is every one of these files ignored by CI?". - Extraction without a YAML dependency: scan for every `paths-ignore:` key and take the `- '…'` items indented under it. Require at least one block and require all blocks to have identical contents; if they differ (someone diverged the `push` filter from the `pull_request` one), that is not a rule this helper can state. - Pattern translation, a whitelist of the two shapes actually in use: `<prefix>/**` → the file starts with `<prefix>/`; `**/*.<ext>` → the file ends with `.<ext>`. **Any other pattern shape is unrecognised.** - **Fail closed, every time.** Unrecognised pattern, fetch failure, parse failure, no block found, blocks disagreeing, or an empty changed-file list → answer "not ignorable", log why, and fall through to the existing behaviour (wait for CI, which for a docs-only branch means the ticket sits until the next tick and is retried). Never "no job found means fine" — that is the hole `docs/direction.md:115` exists to close. 2. **Use it in place of the hardcoded predicate** at `tools/autopilot.ts:287`, and carry its answer forward: hold the result in a local alongside `merged` (`:250`), and at `:309` either call `deployMain(n)` as now, or — when the merge was docs-only — post one comment saying documentation only, nothing to deploy, and skip the call. The label handling at `:305-306` and the merge comment at `:307` are unchanged. Memoise the `ci.yml` fetch for the duration of a `shipReady` pass; it loops over up to 20 issues. 3. **Make it checkable without a live ticket.** There is no test runner for `tools/*.ts` (no `package.json`, no `tsconfig.json`, no CI job runs bun over `tools/`), and `status` does not exercise `shipReady` (`tools/autopilot.ts:1087`). Add a read-only subcommand — `bun tools/autopilot.ts paths <branch-or-sha>` — that prints the patterns it parsed out of `ci.yml`, the changed files it resolved, and the verdict, and exits. It writes nothing and posts nothing. This is the only way an implementer can demonstrate the rule is right before it decides a real deploy. 4. **Documentation.** `docs/workflow.md:137` says the lane runs `tools/deploy-main` after a merge; amend it to say that a documentation-only merge skips it. Record the standing fact in `docs/notes/ci-build-cost.md` (which already owns `paths-ignore` at `:86`): the filter now has readers outside the workflow file, so a pattern added there is a pattern `autopilot.ts` must be able to translate, and one it cannot translate stops the lane rather than deploying blind. Fix the stale prose copy of the rule in the `mainVerdict` comment at `tools/autopilot.ts:421-424` to point at the helper rather than restating `docs/**` and `*.md`. Note that `tools/autopilot.ts` is inside the autopilot's own fence (`FENCED`, `tools/autopilot.ts:212`) and `.forgejo/workflows/` is too (`:211`), so this branch keeps its branch and loses `ship` by design. It lands for review. Do not apply the `ship` label. ## Acceptance criteria - [ ] The strings `docs/` and `.md` no longer appear as a path rule anywhere in `tools/autopilot.ts` — the only source of the patterns is `.forgejo/workflows/ci.yml` on `main`, fetched at run time. - [ ] `tools/deploy-main` is unmodified. `git diff main -- tools/deploy-main` is empty. - [ ] `shipReady`'s existing docs-only merge path (`tools/autopilot.ts:279-292`) behaves as before for a branch whose files are all under `docs/` or end in `.md`: it merges without a verdict. - [ ] After a docs-only merge, `deployMain` is **not** called, and exactly one comment is posted in its place. It says the change is documentation only and there is nothing to deploy, and it does not claim a failure or that "the public services are still on the previous build". - [ ] After a merge that is **not** docs-only, `deployMain(n)` is called exactly as it is today, with no new arguments and no change to `tools/autopilot.ts:389-415`. - [ ] Each of these makes the helper answer "not ignorable" (verified through the new `paths` subcommand, one run per case): `ci.yml` cannot be fetched; the file has no `paths-ignore` block; the two blocks in it differ; a pattern is present that is neither `<prefix>/**` nor `**/*.<ext>`; the changed-file list is empty. Each logs which of those it was. - [ ] `bun tools/autopilot.ts paths 67c0c14` reports the two patterns from `ci.yml`, the one changed file `docs/notes/road-junction-geometry.md`, and a verdict of "ignored by CI". It posts no comment and changes no label. - [ ] `bun tools/autopilot.ts paths ab430c5` (one changed file, `tools/autopilot.ts`) reports "not ignored". - [ ] `bun tools/autopilot.ts status` still runs and prints the same shape of JSON. - [ ] `docs/workflow.md:137`'s paragraph states the docs-only skip. - [ ] `docs/notes/ci-build-cost.md` records that `paths-ignore` has a reader outside the workflow file, and that an unrecognised pattern stops the lane rather than deploying blind. - [ ] The `mainVerdict` comment at `tools/autopilot.ts:421-424` no longer restates the rule in prose. - [ ] The branch is left with `agent:done` and **without** `ship`. ## Verification Read-only, no Rust involved; nothing here needs a GPU, a shot or a workstation. ```bash # type/parse check — there is no test runner for tools/, so this is the floor bun --print 'null' >/dev/null # confirms bun is present bun build --target=bun tools/autopilot.ts --outfile=/dev/null # the rule, against real commits (needs FORGEJO_TOKEN in the environment; # both of these are public commits and the call is a GET) bun tools/autopilot.ts paths 67c0c14 # docs-only merge → ignored by CI bun tools/autopilot.ts paths ab430c5 # tools/ merge → not ignored bun tools/autopilot.ts paths 20b036a # mixed merge → not ignored # the fail-closed arms: point the helper at a doctored ci.yml # (however the implementer chooses to inject one — an env override on the # fetch URL, or a fixture path — the five cases above must each be shown) bun tools/autopilot.ts status # unchanged output shape ``` Not checkable here, and it is what the issue says success is: **the next documentation-only ticket the lane ships posts no failure comment and does not stall.** That can only be seen in the lane's own comments after the change is copied into the running container. **The deployed copy is a manual step.** `docs/workflow.md:854` records that `/data/agent/autopilot/autopilot.ts` is a copy of `tools/autopilot.ts`; the mount is read-only and the container picks up a new file on its next tick. Merging this branch changes nothing until that copy is made, so the fix does not take effect on merge alone. Say so when handing the branch over. ## Out of scope - **Changing `tools/deploy-main` at all**, including its message. A human running it by hand right after a documentation-only merge will still wait `--timeout-min` and get `refusing: no Actions job ran`. That residual is deliberate: it is the untouched gate, it costs a person a `Ctrl-C` rather than costing the lane 45 minutes six times a day, and closing it means putting the path rule inside the refusal gate, which the issue and `docs/direction.md:115-119` both warn against. - Lowering `timeout_min`, or any other change to how long the deploy waits. - Making `deploy-main` accept a `--sha`, or having the autopilot pass one. - Anything about the `FENCED` table, the `ship` label, or the lane's other gates. - Adding a test runner, `package.json` or `tsconfig.json` for `tools/`. - The `.forgejo/workflows/ci.yml` filter itself — no pattern is added, removed or reworded. ## Open questions None. --- Branch: `fix/219-autopilot-docs-only-deploy` <details><summary>Original request</summary> Found by the 3-day retrospective on #218, reading the lane's own comments for 2026-08-29 and 2026-08-30. ## What happens Six tickets in the period ended with this comment, minutes-to-an-hour after a clean merge: > 🤖 **Merged, but not deployed.** The deploy step exited 1: > ``` > main is at 101525c2 > waiting for CI on 101525c2 > refusing: no Actions job ran for 101525c2 — nothing has verified this commit > ``` #186, #192, #176, #197, #199 and #201. Every one of them is a **documentation-only** change — a `docs/notes/` file and a row in `docs/direction.md` recording that a source was checked and not taken. There was nothing to deploy and nothing to verify. ## Why it happens `.forgejo/workflows/ci.yml` has a `paths-ignore` rule, and states its reason at length: a Markdown change should not compile Bevy. So a docs-only merge commit on `main` correctly gets **no Actions job at all**. `tools/autopilot.ts` already knows this. `shipReady` compares the branch against `main`, sees that every changed file is under `docs/` or ends in `.md`, logs *"documentation only — CI does not run on these, shipping"* and merges without a verdict. That is right. `tools/deploy-main` does not know it. Its gate (around line 130) loops until a job appears, and when none ever does it exits 1 with `refusing: no Actions job ran`. That is also right *in isolation* — it is the load-bearing second check `docs/direction.md` describes, the one that stops a timed-out CI wait from reaching the deploy, and it must not be weakened. The two halves simply disagree about what a docs-only commit is. ## What it costs Two things, and the second is the one that matters. - **A false alarm on six tickets.** "Merged, but not deployed … the public services are still on the previous build" reads as a failed deploy. Nothing failed; there was nothing to publish. A message that cries wolf six times in a day is one nobody reads the seventh time — and the seventh will be a real refusal. - **The ship tick blocks for the whole timeout.** `deployMain` is awaited inside `shipReady`'s loop, and the wait runs to `timeout_min` before refusing. #186 merged at 20:51 and the refusal landed at 21:35; #176 merged at 21:52 and refused at 22:36. That is ~45 minutes of the lane sitting still, per docs-only ticket, six times in this period. ## Where the seam is - `tools/deploy-main` — the wait loop and `refusing: no Actions job ran` - `tools/autopilot.ts` — `shipReady`'s docs-only branch (the rule already exists there), and `deployMain` - `.forgejo/workflows/ci.yml` — the `paths-ignore` block the rule has to agree with ## What would settle it Either half works; whichever is chosen, **the two must read the same rule from one place** rather than each carrying its own copy of "docs/ or *.md", because the next path added to `paths-ignore` will otherwise re-open this. 1. **`deploy-main` learns the rule.** Compare the commit's changed files against `paths-ignore`; if every one is ignored, say *"nothing to deploy: documentation only"* and exit **0**. It must stay a whitelist of ignored paths, never "no job found means fine" — that is exactly the hole the gate exists to close. 2. **Or the autopilot does not call it.** `shipReady` has already computed the file list by the time it merges; when the merge was docs-only, skip `deployMain` and comment *"documentation only — nothing to deploy"*. Success is judged from the lane's own comments, not from a picture: the next docs-only ticket merges and posts no failure, and the ship tick does not stall for the timeout. ## A note on labels Not `ship`. `tools/autopilot.ts` and `tools/deploy-main` are both inside the autopilot's own fence (`autopilot.ts`, the `FENCED` table), so a branch touching them keeps its branch and loses `ship` by design. It lands for review, which is the point. <sub>Filed by the retrospective on #218.</sub> </details> <sub>🤖 Refined by the viberfox issue agent. Reply with **@agent refine** and what is wrong to have this rewritten.</sub>
Author
Collaborator

🤖 Refining this before anything gets built — reading the code, no branch, no commits. If it turns out I'd have to guess at the requirements I'll stop and ask instead.

🤖 Refining this before anything gets built — reading the code, no branch, no commits. If it turns out I'd have to guess at the requirements I'll stop and ask instead.
Author
Collaborator

🤖 Refined. The description above is now the spec; I'll pick it up for implementation on the next poll.

If the direction is wrong, say so now — @agent refine with a correction rewrites it, and agent:skip stops me.


🤖 viberfox issue agent on the VPS — 19 turns · $2.01 · session 68bbdae2-efde-46ae-8075-137f3c575b0d. Reply with @agent to continue this session.

🤖 **Refined.** The description above is now the spec; I'll pick it up for implementation on the next poll. If the direction is wrong, say so now — **@agent refine** with a correction rewrites it, and `agent:skip` stops me. --- <sub>🤖 viberfox issue agent on the VPS — 19 turns · $2.01 · session `68bbdae2-efde-46ae-8075-137f3c575b0d`. Reply with **@agent** to continue this session.</sub>
Author
Collaborator

🤖 Picked this up — working on branch fix/219-autopilot-docs-only-deploy in a fresh worktree on the VPS. I'll report back here when I'm done.

Label an issue agent:skip to keep me off it, or driven to keep it in a session you are driving.

🤖 Picked this up — working on branch `fix/219-autopilot-docs-only-deploy` in a fresh worktree on the VPS. I'll report back here when I'm done. <sub>Label an issue `agent:skip` to keep me off it, or `driven` to keep it in a session you are driving.</sub>
Author
Collaborator

The autopilot no longer calls the deploy script after a merge that CI does not build, and the rule for "does CI build this?" is now read from the workflow file instead of being written down a second time in the autopilot.

What changed — three files, no Rust.

  • tools/autopilot.ts: a new helper fetches .forgejo/workflows/ci.yml from main at run time and parses the paths-ignore items out of it. It replaces the hand-written docs/ / .md test that was in shipReady. When a merge was one of those, deployMain is not called; one comment is posted saying the change is documentation only and there is nothing to deploy. Any other merge deploys exactly as before — deployMain itself is unchanged.
  • docs/workflow.md: the paragraph about the deploy step now states the skip.
  • docs/notes/ci-build-cost.md: a new section recording that the paths-ignore block now has a reader outside the workflow, so a pattern added there must be one the autopilot can translate, and one it cannot stops the lane rather than deploying blind.

tools/deploy-main is untouched. git diff origin/main -- tools/deploy-main is empty. Its refusal behaviour is bit-identical; it is simply not called for these commits.

Fails closed in six ways. Workflow unfetchable, no paths-ignore block, an empty one, the two copies disagreeing, a pattern shape it does not recognise, or an empty changed-file list — each answers "not ignorable", logs which one it was, and the branch goes back to waiting for CI.

How I verified it. There is no test runner for tools/, so I added a read-only subcommand and ran it. bun build --target=bun tools/autopilot.ts parses. Against real commits:

  • paths 67c0c14 → patterns docs/**, **/*.md; one file docs/notes/road-junction-geometry.md; verdict ignored by CI
  • paths ab430c5 → one file tools/autopilot.ts; verdict not ignored
  • paths 20b036a → two files, one of them tools/autopilot.ts; verdict not ignored

All five fail-closed cases were run one at a time against doctored workflow files via CARTO_AUTOPILOT_CI_WORKFLOW, and each printed its own reason. status still prints the same JSON.

What is not verified. The end-to-end path — a real documentation ticket merging and posting the new comment instead of stalling — cannot be exercised here; it needs a live ticket with a ship label. The success test the issue names is the lane's own comments after the next documentation-only ticket.

One thing to do by hand. The running container reads /data/agent/autopilot/autopilot.ts, a copy of this file. Merging this branch changes nothing until that copy is made; the container picks it up on its next tick.

The branch is fix/219-autopilot-docs-only-deploy, pushed. It touches tools/autopilot.ts, which is inside the autopilot's own fence, so it keeps its branch and does not get the ship label — it lands for your review.

Branch fix/219-autopilot-docs-only-deploy (pushed)

a9599f3 fix(autopilot): read the CI path filter instead of copying it

🤖 viberfox issue agent on the VPS — 35 turns · $3.43 · session 3ac571e7-9dc9-4348-8cf1-7bae9c715b3e. Reply with @agent to continue this session.

The autopilot no longer calls the deploy script after a merge that CI does not build, and the rule for "does CI build this?" is now read from the workflow file instead of being written down a second time in the autopilot. **What changed** — three files, no Rust. - `tools/autopilot.ts`: a new helper fetches `.forgejo/workflows/ci.yml` from `main` at run time and parses the `paths-ignore` items out of it. It replaces the hand-written `docs/` / `.md` test that was in `shipReady`. When a merge was one of those, `deployMain` is not called; one comment is posted saying the change is documentation only and there is nothing to deploy. Any other merge deploys exactly as before — `deployMain` itself is unchanged. - `docs/workflow.md`: the paragraph about the deploy step now states the skip. - `docs/notes/ci-build-cost.md`: a new section recording that the `paths-ignore` block now has a reader outside the workflow, so a pattern added there must be one the autopilot can translate, and one it cannot stops the lane rather than deploying blind. **`tools/deploy-main` is untouched.** `git diff origin/main -- tools/deploy-main` is empty. Its refusal behaviour is bit-identical; it is simply not called for these commits. **Fails closed in six ways.** Workflow unfetchable, no `paths-ignore` block, an empty one, the two copies disagreeing, a pattern shape it does not recognise, or an empty changed-file list — each answers "not ignorable", logs which one it was, and the branch goes back to waiting for CI. **How I verified it.** There is no test runner for `tools/`, so I added a read-only subcommand and ran it. `bun build --target=bun tools/autopilot.ts` parses. Against real commits: - `paths 67c0c14` → patterns `docs/**`, `**/*.md`; one file `docs/notes/road-junction-geometry.md`; verdict **ignored by CI** - `paths ab430c5` → one file `tools/autopilot.ts`; verdict **not ignored** - `paths 20b036a` → two files, one of them `tools/autopilot.ts`; verdict **not ignored** All five fail-closed cases were run one at a time against doctored workflow files via `CARTO_AUTOPILOT_CI_WORKFLOW`, and each printed its own reason. `status` still prints the same JSON. **What is not verified.** The end-to-end path — a real documentation ticket merging and posting the new comment instead of stalling — cannot be exercised here; it needs a live ticket with a `ship` label. The success test the issue names is the lane's own comments after the next documentation-only ticket. **One thing to do by hand.** The running container reads `/data/agent/autopilot/autopilot.ts`, a copy of this file. Merging this branch changes nothing until that copy is made; the container picks it up on its next tick. The branch is `fix/219-autopilot-docs-only-deploy`, pushed. It touches `tools/autopilot.ts`, which is inside the autopilot's own fence, so it keeps its branch and does not get the `ship` label — it lands for your review. **Branch** `fix/219-autopilot-docs-only-deploy` (pushed) ``` a9599f3 fix(autopilot): read the CI path filter instead of copying it ``` --- <sub>🤖 viberfox issue agent on the VPS — 35 turns · $3.43 · session `3ac571e7-9dc9-4348-8cf1-7bae9c715b3e`. Reply with **@agent** to continue this session.</sub>
Author
Collaborator

🤖 Marked agent:done without a CI verdict on a9599f3c — no workflow reported on this commit. The branch is waiting for review; check the run yourself before landing it.

🤖 Marked `agent:done` **without a CI verdict** on `a9599f3c` — no workflow reported on this commit. The branch is waiting for review; check the run yourself before landing it.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jeroen/cartopolis#219
No description provided.