specverb cannot reach the Forgejo Actions log surface: it is not in the swagger, and the only working route needs Basic auth + a non-REST body #223

Open
opened 2026-07-12 02:29:01 +00:00 by coilyco-ops · 1 comment
Member

The gap

The guarded Forgejo ops surface is built by specverb from the swagger spec, so a verb can only exist if the upstream OpenAPI declares it. The Actions log surface is not in Forgejo's swagger, and the REST routes that would carry it 404 on this Forgejo even with a valid token:

GET  /api/v1/repos/{o}/{r}/actions/runs/{runID}/jobs/{jobID}/attempt/{n}/logs  -> 404
GET  /api/v1/repos/{o}/{r}/actions/runs/{runIdx}/jobs/{jobIdx}/logs            -> 404
GET  /api/v1/repos/{o}/{r}/actions/jobs/{jobID}/logs                           -> 404
GET  /api/v1/repos/{o}/{r}/actions/runs/{id}/jobs                              -> 404
GET  /api/v1/repos/{o}/{r}/actions/runs/{id}/artifacts                         -> 404
POST /api/v1/repos/{o}/{r}/actions/runs/{id}/rerun-failed-jobs                 -> 404

(The same token gets 200 from /actions/tasks, so this is the routes, not auth. And it 404s for successful runs and minutes-old runs, so it is not log expiry.)

Consumers work around it by shelling out to a bridge script (agentic-os ships forgejo-actions-logs.sh -> python3 -m agentic_os.forgejo_actions_logs), wrapped as an exec-dialect can run "actions logs" grant. Those bridges target the 404 routes above, so they are dead - an agent cannot read its own CI logs today. Filed as agentic-os#473.

Why this is awkward for specverb

The only thing that works is the web UI's ajax endpoint, and it breaks three assumptions the guardfile/specverb model makes:

  1. Not under /api/v1 - it is a web route: /{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}/attempt/{n}.
  2. Basic auth only - Authorization: token <pat> gets a 307 redirect to the login page on web routes. -u user:token works. The guardfile's auth header-token shape cannot express that.
  3. A POST with a cursor body, not a REST read - {"logCursors":[{"step":N,"cursor":0,"expanded":true}]}, returning logs.stepsLog[].lines[].message. And the ids are the display run/job indices, not the internal ones the API uses.

So it is a read, but it is a POST, to a non-API host path, with a different auth scheme, keyed on different ids. specverb has no way to declare that from a spec, and the exec-dialect escape hatch (shell out to a bridge) is what everyone reaches for - which is why the bridge exists and why nobody noticed it had rotted.

Ask

Decide where this belongs:

  • teach the guardfile dialect an auth mode + non-swagger route escape so a log verb can be declared and guarded like any other leaf, or
  • accept that it stays an exec-dialect bridge, and say so explicitly in the docs so the next person does not assume specverb covers it.

Either way the current state is the worst one: a guarded verb (can run "actions logs") that is declared, looks supported, and silently 404s.

Context

Hit while diagnosing a publish-dev-base failure that had been invisible for two days (agentic-os#452). The only way to read the build log was hand-rolling the ajax call with Basic auth. Rerun had no working route at all - the fallback was POST /actions/workflows/{file}/dispatches, which re-runs the whole workflow instead of the failed job.

## The gap The guarded Forgejo ops surface is built by `specverb` from the swagger spec, so a verb can only exist if the upstream OpenAPI declares it. The **Actions log surface is not in Forgejo's swagger**, and the REST routes that would carry it **404 on this Forgejo** even with a valid token: ``` GET /api/v1/repos/{o}/{r}/actions/runs/{runID}/jobs/{jobID}/attempt/{n}/logs -> 404 GET /api/v1/repos/{o}/{r}/actions/runs/{runIdx}/jobs/{jobIdx}/logs -> 404 GET /api/v1/repos/{o}/{r}/actions/jobs/{jobID}/logs -> 404 GET /api/v1/repos/{o}/{r}/actions/runs/{id}/jobs -> 404 GET /api/v1/repos/{o}/{r}/actions/runs/{id}/artifacts -> 404 POST /api/v1/repos/{o}/{r}/actions/runs/{id}/rerun-failed-jobs -> 404 ``` (The same token gets 200 from `/actions/tasks`, so this is the routes, not auth. And it 404s for **successful** runs and minutes-old runs, so it is not log expiry.) Consumers work around it by shelling out to a bridge script (agentic-os ships `forgejo-actions-logs.sh` -> `python3 -m agentic_os.forgejo_actions_logs`), wrapped as an exec-dialect `can run "actions logs"` grant. Those bridges target the 404 routes above, so they are **dead** - an agent cannot read its own CI logs today. Filed as agentic-os#473. ## Why this is awkward for specverb The **only** thing that works is the web UI's ajax endpoint, and it breaks three assumptions the guardfile/specverb model makes: 1. **Not under `/api/v1`** - it is a web route: `/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}/attempt/{n}`. 2. **Basic auth only** - `Authorization: token <pat>` gets a **307 redirect to the login page** on web routes. `-u user:token` works. The guardfile's `auth header-token` shape cannot express that. 3. **A POST with a cursor body, not a REST read** - `{"logCursors":[{"step":N,"cursor":0,"expanded":true}]}`, returning `logs.stepsLog[].lines[].message`. And the ids are the *display* run/job indices, not the internal ones the API uses. So it is a read, but it is a POST, to a non-API host path, with a different auth scheme, keyed on different ids. specverb has no way to declare that from a spec, and the exec-dialect escape hatch (shell out to a bridge) is what everyone reaches for - which is why the bridge exists and why nobody noticed it had rotted. ## Ask Decide where this belongs: * teach the guardfile dialect an auth mode + non-swagger route escape so a log verb can be declared and guarded like any other leaf, or * accept that it stays an exec-dialect bridge, and say so explicitly in the docs so the next person does not assume specverb covers it. Either way the current state is the worst one: a guarded verb (`can run "actions logs"`) that is *declared*, looks supported, and silently 404s. ## Context Hit while diagnosing a `publish-dev-base` failure that had been invisible for two days (agentic-os#452). The only way to read the build log was hand-rolling the ajax call with Basic auth. Rerun had no working route at all - the fallback was `POST /actions/workflows/{file}/dispatches`, which re-runs the whole workflow instead of the failed job.
Author
Member

Sibling issue on the deployment side: agentic-os#473 - every Forgejo Actions log/rerun API route the .ward bridges target 404s, including on successful runs. That is the concrete failure this engine-side gap produces in practice; the working workaround (web POST + Basic auth) is documented there.

Sibling issue on the deployment side: agentic-os#473 - every Forgejo Actions log/rerun API route the `.ward` bridges target 404s, including on successful runs. That is the concrete failure this engine-side gap produces in practice; the working workaround (web POST + Basic auth) is documented there.
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
coilyco-flight-deck/cli-guard#223
No description provided.