The Forgejo Actions log + rerun bridges are dead: every API route they target 404s, including on successful runs #473

Closed
opened 2026-07-12 02:28:27 +00:00 by coilyco-ops · 4 comments
Member

Symptom

.ward/forgejo-actions-logs.sh and .ward/forgejo-actions-rerun*.sh (and the agentic_os.forgejo_actions_logs / agentic_os.forgejo_actions_rerun modules behind them) target Forgejo API routes this Forgejo does not serve. Every one returns 404, with a valid coilyco-ops token that works fine elsewhere.

An agent currently cannot read CI logs at all, and cannot re-run a failed job.

Evidence

Token is good - the tasks endpoint answers 200:

GET /api/v1/repos/coilyco-flight-deck/agentic-os/actions/tasks?limit=1   -> 200

Every log/rerun/artifact route 404s:

GET  /api/v1/repos/{o}/{r}/actions/runs/7174/jobs/11659/attempt/1/logs   -> 404
GET  /api/v1/repos/{o}/{r}/actions/runs/1055/jobs/2/attempt/1/logs       -> 404
GET  /api/v1/repos/{o}/{r}/actions/runs/1055/jobs/2/logs                 -> 404
GET  /api/v1/repos/{o}/{r}/actions/jobs/11659/logs                       -> 404
GET  /api/v1/repos/{o}/{r}/actions/runs/1055/artifacts                   -> 404
GET  /api/v1/repos/{o}/{r}/actions/runs/{id}/jobs                        -> 404
POST /api/v1/repos/{o}/{r}/actions/runs/1815/rerun-failed-jobs           -> 404

This is not log expiry: it 404s for successful runs and for runs minutes old, exactly as it does for old failed ones. The route simply is not there.

forgejo_actions_logs.py even resolves the internal ids correctly first (it scrapes data-run-id / data-job-index off the HTML), then dies on the final fetch:

Forgejo returned 404 for the resolved job log route.
status target /actions/runs/1815/jobs/0 resolved to run_id=7063 job_id=11545.

What does work (the workaround I had to use)

The web UI's own ajax endpoint, with Basic auth (-u coilyco-ops:$TOKEN). Note Authorization: token ... gets a 307 redirect to login on web routes - only Basic works:

# step statuses: GET the job page, parse data-initial-post-response
curl -u "coilyco-ops:$TOKEN" \n  https://forgejo.coilysiren.me/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}

# step logs: POST the same path + /attempt/{n}
curl -u "coilyco-ops:$TOKEN" -X POST -H 'Content-Type: application/json' \n  -d '{"logCursors":[{"step":7,"cursor":0,"expanded":true}]}' \n  https://forgejo.coilysiren.me/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}/attempt/1

That returns JSON with logs.stepsLog[].lines[].message - the full plaintext stream. It is what I used to read the publish-dev-base failure that had been invisible for two days (aos#452). Without it that bug stays undiagnosable.

The /attempt/{n} suffix matters: without it the server looks up attempt 0 and 500s with task with job_id N and attempt 0: resource does not exist.

Why it matters

Every workflow in this repo posts a Telegram alert on a red main, and the alert links a run URL an agent then cannot read. The whole point of guardfile.forgejo.readactions.kdl (can run "actions logs") is that a headless agent can diagnose its own CI. Today it cannot, and the failure mode is silent: the bridge exits non-zero with a 404 and the agent has no path forward.

Suggested fix

Repoint the bridges at the web ajax endpoint above (Basic auth, /attempt/{n}, parse stepsLog), or confirm whether this Forgejo version simply lacks the API log routes and pin the bridge to whatever it does serve. Rerun likewise needs a working route (I had to fall back to POST /actions/workflows/{file}/dispatches, which re-runs the whole workflow rather than the failed job).

See the sibling issue on cli-guard for the guardfile/verb side.

## Symptom `.ward/forgejo-actions-logs.sh` and `.ward/forgejo-actions-rerun*.sh` (and the `agentic_os.forgejo_actions_logs` / `agentic_os.forgejo_actions_rerun` modules behind them) target Forgejo API routes this Forgejo **does not serve**. Every one returns 404, with a valid `coilyco-ops` token that works fine elsewhere. An agent currently **cannot read CI logs at all**, and cannot re-run a failed job. ## Evidence Token is good - the tasks endpoint answers 200: ``` GET /api/v1/repos/coilyco-flight-deck/agentic-os/actions/tasks?limit=1 -> 200 ``` Every log/rerun/artifact route 404s: ``` GET /api/v1/repos/{o}/{r}/actions/runs/7174/jobs/11659/attempt/1/logs -> 404 GET /api/v1/repos/{o}/{r}/actions/runs/1055/jobs/2/attempt/1/logs -> 404 GET /api/v1/repos/{o}/{r}/actions/runs/1055/jobs/2/logs -> 404 GET /api/v1/repos/{o}/{r}/actions/jobs/11659/logs -> 404 GET /api/v1/repos/{o}/{r}/actions/runs/1055/artifacts -> 404 GET /api/v1/repos/{o}/{r}/actions/runs/{id}/jobs -> 404 POST /api/v1/repos/{o}/{r}/actions/runs/1815/rerun-failed-jobs -> 404 ``` This is **not** log expiry: it 404s for *successful* runs and for runs minutes old, exactly as it does for old failed ones. The route simply is not there. `forgejo_actions_logs.py` even resolves the internal ids correctly first (it scrapes `data-run-id` / `data-job-index` off the HTML), then dies on the final fetch: ``` Forgejo returned 404 for the resolved job log route. status target /actions/runs/1815/jobs/0 resolved to run_id=7063 job_id=11545. ``` ## What does work (the workaround I had to use) The **web UI's own ajax endpoint**, with **Basic** auth (`-u coilyco-ops:$TOKEN`). Note `Authorization: token ...` gets a **307 redirect to login** on web routes - only Basic works: ```bash # step statuses: GET the job page, parse data-initial-post-response curl -u "coilyco-ops:$TOKEN" \n https://forgejo.coilysiren.me/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex} # step logs: POST the same path + /attempt/{n} curl -u "coilyco-ops:$TOKEN" -X POST -H 'Content-Type: application/json' \n -d '{"logCursors":[{"step":7,"cursor":0,"expanded":true}]}' \n https://forgejo.coilysiren.me/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}/attempt/1 ``` That returns JSON with `logs.stepsLog[].lines[].message` - the full plaintext stream. It is what I used to read the `publish-dev-base` failure that had been invisible for two days (aos#452). Without it that bug stays undiagnosable. The `/attempt/{n}` suffix matters: without it the server looks up `attempt 0` and 500s with `task with job_id N and attempt 0: resource does not exist`. ## Why it matters Every workflow in this repo posts a Telegram alert on a red main, and the alert links a run URL an agent then cannot read. The whole point of `guardfile.forgejo.readactions.kdl` (`can run "actions logs"`) is that a headless agent can diagnose its own CI. Today it cannot, and the failure mode is silent: the bridge exits non-zero with a 404 and the agent has no path forward. ## Suggested fix Repoint the bridges at the web ajax endpoint above (Basic auth, `/attempt/{n}`, parse `stepsLog`), or confirm whether this Forgejo version simply lacks the API log routes and pin the bridge to whatever it does serve. Rerun likewise needs a working route (I had to fall back to `POST /actions/workflows/{file}/dispatches`, which re-runs the whole workflow rather than the failed job). See the sibling issue on cli-guard for the guardfile/verb side.
Author
Member

Sibling issue on the engine side: cli-guard#223 - specverb cannot declare this surface at all (it is not in Forgejo swagger, the only working route is a web POST needing Basic auth), which is why it lives as an exec-dialect bridge here.

Sibling issue on the engine side: cli-guard#223 - specverb cannot declare this surface at all (it is not in Forgejo swagger, the only working route is a web POST needing Basic auth), which is why it lives as an exec-dialect bridge here.
Author
Member

WARDED_WORKFLOW: reservation-held

reservation details

Holder: launch intent for container engineer-codex-agentic-os-473 on host kais-macbook-pro-2.local.

Accepted by ward agent --harness codex (reserved 2026-07-14T10:39:23Z). Concurrent ward agent runs are blocked until this intent becomes visible or the intent is released. The stale-intent fallback is still TTL-bounded (3h TTL). --override-reservation overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilyco-flight-deck/agentic-os#473 · branch issue-473 · harness codex · workflow pull-request-and-merge
  • Run: engineer-codex-agentic-os-473 · ward v0.679.0 · dispatched 2026-07-14T10:39:18Z
  • Comment thread: 1 included in the pre-flight read, 0 stripped (ward's own automated comments).

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.679.0).

— Codex, via ward agent

<!-- ward-agent-reservation --> WARDED_WORKFLOW: reservation-held <details><summary>reservation details</summary> Holder: launch intent for container `engineer-codex-agentic-os-473` on host `kais-macbook-pro-2.local`. Accepted by `ward agent --harness codex` (reserved 2026-07-14T10:39:23Z). Concurrent `ward agent` runs are blocked until this intent becomes visible or the intent is released. The stale-intent fallback is still TTL-bounded (3h TTL). `--override-reservation` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh**. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilyco-flight-deck/agentic-os#473` · branch `issue-473` · harness `codex` · workflow `pull-request-and-merge` - **Run:** `engineer-codex-agentic-os-473` · ward `v0.679.0` · dispatched `2026-07-14T10:39:18Z` - **Comment thread:** 1 included in the pre-flight read, 0 stripped (ward's own automated comments). - included: @coilyco-ops (2026-07-12T02:29:21Z) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.679.0). </details> </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Owner
Implement this with specverb fetch: https://forgejo.coilysiren.me/coilyco-flight-deck/cli-guard/src/branch/main/docs/specverb-fetch.md
Author
Member

WARDED_WORKFLOW: #529

details

workflow: pull-request-and-merge, review summary: skipped, review gate skipped by ~/.ward/config.yaml default
felt: the bridge work settled after switching the rerun metadata lookup to the internal run id, then the workflow dispatch fallback matched the deployed Forgejo behavior.
confidence: high
surprises: the visible run number was not the API key, and the rerun control 404'd here.
follow-ups: none

WARDED_WORKFLOW: https://forgejo.coilysiren.me/coilyco-flight-deck/agentic-os/pulls/529 <details><summary>details</summary> workflow: pull-request-and-merge, review summary: skipped, review gate skipped by ~/.ward/config.yaml default felt: the bridge work settled after switching the rerun metadata lookup to the internal run id, then the workflow dispatch fallback matched the deployed Forgejo behavior. confidence: high surprises: the visible run number was not the API key, and the rerun control 404'd here. follow-ups: none </details>
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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
coilyco-flight-deck/agentic-os#473
No description provided.