Runtime 403/404 from the resolve endpoint is misclassified as "resolution unsupported" #8

Open
opened 2026-08-25 15:36:19 +00:00 by jercik · 2 comments
Owner

During the j4k/review pilot probe (j4k/review#71), the wrapper at pin e40abca1334188538aeade64a16d76d54ec35c9e logged on both reconcile passes:

review-wrapper: conversation resolution is unavailable on this instance — projecting replies only for the rest of this pass

and left every finding conversation with resolver: null.

The server is 16.0.2-j4k.1+gitea-1.22.0 — a -j4k fork that has the conversation-resolution REST API — and fgj pr review resolve resolved those same conversations minutes later. So the API is present and working; the wrapper's capability detection is what's wrong.

Effect: on every enrolled repository, superseded findings get a reply comment but stay visually unresolved, and someone has to resolve them by hand.

Observed on runs 32755 (reconcile at 14:32:34Z) and 32804 (15:05:17Z) on 2026-08-25.

During the j4k/review pilot probe ([j4k/review#71](https://code.j4k.dev/j4k/review/pulls/71)), the wrapper at pin `e40abca1334188538aeade64a16d76d54ec35c9e` logged on both reconcile passes: ``` review-wrapper: conversation resolution is unavailable on this instance — projecting replies only for the rest of this pass ``` and left every finding conversation with `resolver: null`. The server is `16.0.2-j4k.1+gitea-1.22.0` — a `-j4k` fork that has the conversation-resolution REST API — and `fgj pr review resolve` resolved those same conversations minutes later. So the API is present and working; the wrapper's capability detection is what's wrong. Effect: on every enrolled repository, superseded findings get a reply comment but stay visually unresolved, and someone has to resolve them by hand. Observed on runs 32755 (reconcile at 14:32:34Z) and 32804 (15:05:17Z) on 2026-08-25.
Author
Owner

Two corrections from investigating this while enrolling j4k/dynamic-tools (PR #7), both narrowing where the fix goes.

The version probe is not what fails. probeResolution (src/forge/client.ts:73-81) tests meta.version.includes("-j4k"), and this instance answers {"version":"16.0.2-j4k.1+gitea-1.22.0", …} — so it returns true. The title's "misdetected" points at the wrong line.

The receipts confirm which branch fired. Both runs recorded here logged:

review-wrapper: conversation resolution is unavailable on this instance — projecting replies only for the rest of this pass

That string is emitted only from the resolve()/unresolve() catch block in src/reconcile/dispositions.ts:47-53. The supportsResolution() === false branch prints a different one — "this instance has no conversation-resolution API — projecting dispositions as replies only" (dispositions.ts:156-158) — and it does not appear. Probe passed; the POST …/reviews/{id}/comments/{comment}/resolution itself was refused. The route exists on the server: swagger.v1.json documents repoResolvePullReviewComment with 200, 403, 404.

The blocker to diagnosing it further is the logging. isResolutionDegradation (src/reconcile/conversation-markers.ts:58-64) collapses 403 and 404 into one verdict:

return shape.resolutionUnsupported === true || shape.status === 403 || shape.status === 404;

and the catch block logs a fixed string that discards the status and the URL. So the report cannot say whether the Actions task token lacked resolve permission (403) or hit a routing problem (404) — and those need different fixes. Logging the status and the request URL on the degradation path is a prerequisite for closing this.

Worth noting the blast radius while it's open: supported is reassigned from each projection result (dispositions.ts:167,180), so one refusal disables resolution for every remaining conversation in the pass, not just the one that failed.

Two corrections from investigating this while enrolling `j4k/dynamic-tools` ([PR #7](https://code.j4k.dev/j4k/dynamic-tools/pulls/7)), both narrowing where the fix goes. **The version probe is not what fails.** `probeResolution` (`src/forge/client.ts:73-81`) tests `meta.version.includes("-j4k")`, and this instance answers `{"version":"16.0.2-j4k.1+gitea-1.22.0", …}` — so it returns `true`. The title's "misdetected" points at the wrong line. The receipts confirm which branch fired. Both runs recorded here logged: > review-wrapper: conversation resolution is unavailable on this instance — projecting replies only for the rest of this pass That string is emitted only from the `resolve()`/`unresolve()` catch block in `src/reconcile/dispositions.ts:47-53`. The `supportsResolution() === false` branch prints a different one — *"this instance has no conversation-resolution API — projecting dispositions as replies only"* (`dispositions.ts:156-158`) — and it does not appear. Probe passed; the `POST …/reviews/{id}/comments/{comment}/resolution` itself was refused. The route exists on the server: `swagger.v1.json` documents `repoResolvePullReviewComment` with `200`, `403`, `404`. **The blocker to diagnosing it further is the logging.** `isResolutionDegradation` (`src/reconcile/conversation-markers.ts:58-64`) collapses `403` and `404` into one verdict: ```ts return shape.resolutionUnsupported === true || shape.status === 403 || shape.status === 404; ``` and the catch block logs a fixed string that discards the status and the URL. So the report cannot say whether the Actions task token lacked resolve permission (`403`) or hit a routing problem (`404`) — and those need different fixes. Logging the status and the request URL on the degradation path is a prerequisite for closing this. Worth noting the blast radius while it's open: `supported` is reassigned from each projection result (`dispositions.ts:167,180`), so one refusal disables resolution for every remaining conversation in the pass, not just the one that failed.
jercik changed title from Conversation-resolution capability misdetected on code.j4k.dev to Runtime 403/404 from the resolve endpoint is misclassified as "resolution unsupported" 2026-08-31 16:18:42 +00:00
Author
Owner

Fixed in PR #11; retitled to name the real fault, since the version probe was never the bug.

isResolutionDegradation is replaced by classifyResolutionFailure, which takes the probe's verdict as an argument. On a probe-confirmed -j4k instance a 403 or 404 from POST .../reviews/{id}/comments/{comment}/resolution is now logged as a refusal, quoting the status and the endpoint verbatim; "unsupported" is left for instances whose probe found no -j4k. ForgeRequestError carries the request URL so the diagnostic can name it. The pass still degrades to replies rather than failing the run.

That fixes the label, not the refusal. Whatever status code.j4k.dev actually returned will be visible in the next reconcile log on the new pin, and if it is a 403 the Actions task token still needs resolve permission granted server-side. Leaving this open until a run shows the real status and that side is settled.

Fixed in [PR #11](https://code.j4k.dev/j4k-oss/review-wrapper/pulls/11); retitled to name the real fault, since the version probe was never the bug. `isResolutionDegradation` is replaced by `classifyResolutionFailure`, which takes the probe's verdict as an argument. On a probe-confirmed `-j4k` instance a 403 or 404 from `POST .../reviews/{id}/comments/{comment}/resolution` is now logged as a refusal, quoting the status and the endpoint verbatim; "unsupported" is left for instances whose probe found no `-j4k`. `ForgeRequestError` carries the request URL so the diagnostic can name it. The pass still degrades to replies rather than failing the run. That fixes the label, not the refusal. Whatever status `code.j4k.dev` actually returned will be visible in the next reconcile log on the new pin, and if it is a 403 the Actions task token still needs resolve permission granted server-side. Leaving this open until a run shows the real status and that side is settled.
Sign in to join this conversation.
No labels
No milestone
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
j4k-oss/review-wrapper#8
No description provided.