opcore's verb-to-method convention may not know reopen, which would silently emit POST #1656

Closed
opened 2026-08-12 17:22:24 +00:00 by coilyco-ops · 2 comments
Owner

Filed speculatively — verify before fixing

I have not read http/opcore/method.go. This is filed from the convention as documented in a consuming guardfile, because if it holds it is a footgun worth closing, and if it does not the issue costs one read to reject.

coilyco-bridge/deploy#395 asks for a can reopen issue grant in the Forgejo MCP guardfile. This is the ward-side half of that, filed separately because it is a grammar concern rather than a guardfile edit.

Correction, 2026-08-12. An earlier revision of this body cited aosguard resolving reopen to PATCH as evidence that two projections of the same descriptors disagreed. That was wrong — aosguard is a separate tool and shares no machinery with ward. See the correction comment below. The argument here does not depend on it.

The claim

coilyco-bridge/deploy/services/forgejo-mcp/forgejo.mcp.kdl documents the method convention in its header:

Method comes from the verb by convention (get/list/search/view -> GET, create/comment/add and unknown verbs such as pin -> POST, edit/close -> PATCH, set -> PUT, remove -> DELETE). See http/opcore/method.go.

reopen is not in that list. If the table is exactly as described, can reopen issue resolves to POST and not PATCH, so a grant that looks correct in KDL emits the wrong method against the Forgejo API.

Why this is worth a ticket rather than a comment

The fallthrough is silent. A guardfile author writing can reopen issue gets a syntactically valid grant, a minted tool, and a request with the wrong verb — with nothing at parse time saying so.

That is the same hazard class the guardfile header is otherwise careful about:

Keep writes on Forgejo's canonical TLS API URL. opcore refuses redirects for mutating methods so a wrong repository path cannot degrade to a GET.

A mutating method is protected from degrading to a GET via redirect, but an unrecognised verb can be assigned the wrong mutating method by default. The first is guarded and the second is not.

It also generalises past this one grant. Any future verb outside the table — archive, restore, transfer, merge — lands on POST by default, and each one is a fresh instance of the same silent miss.

Possible dispositions, in preference order

  1. reopen is already in the table and the guardfile header's list is merely abbreviated. Close this, and consider correcting the header comment in deploy so the next reader is not misled the same way.
  2. Add reopen -> PATCH to the convention. Smallest real fix and it unblocks deploy#395 as a pure guardfile edit.
  3. Fail closed on an unrecognised verb rather than defaulting to POST. Strictly better than 2 and a larger change: an unknown verb becomes a parse error naming the verb, so the grammar cannot silently guess a mutating method. Would need an audit of existing guardfiles first, since pin is documented as relying on the POST fallthrough and would have to be declared explicitly.

Option 3 is the one that matches the grammar's stated philosophy — deny-by-absence, an unwritten grant is an unminted tool — applied to methods rather than to tools. Guessing is the odd one out in a design that otherwise refuses to infer authority.

Acceptance

  • It is stated in writing whether opcore's verb table contains reopen.
  • If it does not, either the table gains it, or an unrecognised verb stops silently resolving to POST.
  • deploy#395 can add can reopen issue and get a PATCH carrying {"state":"open"}.
  • No existing grant changes method as a side effect. pin's POST behaviour in particular is load-bearing and documented.

Context

Came up in the 2026-08-12 coilyco-gaming/sirens-echo session, where two issues needed reopening and the MCP surface had no grant for it. Full motivation in coilyco-bridge/deploy#395.

## Filed speculatively — verify before fixing I have **not** read `http/opcore/method.go`. This is filed from the convention as documented in a consuming guardfile, because if it holds it is a footgun worth closing, and if it does not the issue costs one read to reject. `coilyco-bridge/deploy#395` asks for a `can reopen issue` grant in the Forgejo MCP guardfile. This is the ward-side half of that, filed separately because it is a grammar concern rather than a guardfile edit. > **Correction, 2026-08-12.** An earlier revision of this body cited `aosguard` resolving `reopen` to PATCH as evidence that two projections of the same descriptors disagreed. That was wrong — aosguard is a separate tool and shares no machinery with ward. See the correction comment below. The argument here does not depend on it. ## The claim `coilyco-bridge/deploy/services/forgejo-mcp/forgejo.mcp.kdl` documents the method convention in its header: > Method comes from the verb by convention (get/list/search/view -> GET, create/comment/add **and unknown verbs such as pin -> POST**, edit/close -> PATCH, set -> PUT, remove -> DELETE). See `http/opcore/method.go`. `reopen` is not in that list. If the table is exactly as described, `can reopen issue` resolves to **POST** and not PATCH, so a grant that looks correct in KDL emits the wrong method against the Forgejo API. ## Why this is worth a ticket rather than a comment The fallthrough is silent. A guardfile author writing `can reopen issue` gets a syntactically valid grant, a minted tool, and a request with the wrong verb — with nothing at parse time saying so. That is the same hazard class the guardfile header is otherwise careful about: > Keep writes on Forgejo's canonical TLS API URL. opcore refuses redirects for mutating methods so a wrong repository path cannot degrade to a GET. A mutating method is protected from *degrading to a GET* via redirect, but an unrecognised verb can be *assigned* the wrong mutating method by default. The first is guarded and the second is not. It also generalises past this one grant. Any future verb outside the table — `archive`, `restore`, `transfer`, `merge` — lands on POST by default, and each one is a fresh instance of the same silent miss. ## Possible dispositions, in preference order 1. **`reopen` is already in the table** and the guardfile header's list is merely abbreviated. Close this, and consider correcting the header comment in `deploy` so the next reader is not misled the same way. 2. **Add `reopen -> PATCH`** to the convention. Smallest real fix and it unblocks `deploy#395` as a pure guardfile edit. 3. **Fail closed on an unrecognised verb** rather than defaulting to POST. Strictly better than 2 and a larger change: an unknown verb becomes a parse error naming the verb, so the grammar cannot silently guess a mutating method. Would need an audit of existing guardfiles first, since `pin` is documented as relying on the POST fallthrough and would have to be declared explicitly. Option 3 is the one that matches the grammar's stated philosophy — deny-by-absence, an unwritten grant is an unminted tool — applied to methods rather than to tools. Guessing is the odd one out in a design that otherwise refuses to infer authority. ## Acceptance * It is stated in writing whether opcore's verb table contains `reopen`. * If it does not, either the table gains it, or an unrecognised verb stops silently resolving to POST. * `deploy#395` can add `can reopen issue` and get a PATCH carrying `{"state":"open"}`. * No existing grant changes method as a side effect. `pin`'s POST behaviour in particular is load-bearing and documented. ## Context Came up in the 2026-08-12 `coilyco-gaming/sirens-echo` session, where two issues needed reopening and the MCP surface had no grant for it. Full motivation in `coilyco-bridge/deploy#395`.
Author
Owner

Correction: aosguard is unrelated to ward, and was never evidence here

The original body carried a section titled "Evidence the two surfaces may already disagree." It argued that because aosguard ops forgejo issue reopen resolves to PATCH, and ward's opcore convention may resolve reopen to POST, the two might be projections of the same []Descriptor in disagreement.

That was wrong. aosguard shares no machinery with ward. It is aos-v0.195.0 — agentic-os — and its verbs are generated by specgen there. Nothing about how it resolves a method says anything about http/opcore/method.go. I have removed the section rather than left it to mislead a reader.

How the conflation happened, since it is the kind of thing worth naming: coilyco-bridge/deploy/AGENTS.md introduces both in one paragraph as two halves of a single gating story — ward exec <verb> for dev commands, aosguard ops <area> for operator ones. Both carry "guard" in the name, both expose "verbs", and both are framed as routing commands through a gate. I inferred shared implementation from shared vocabulary and adjacent documentation without checking, while in the same breath flagging that I had not read method.go. The version string aos-v0.195.0 was quoted in my own text and would have settled it.

What this changes about the issue

Less than it might look like, which is why the issue stands rather than being withdrawn.

The core claim never depended on aosguard. It is a question about one repository's own documented convention: the guardfile header lists the verb-to-method table and reopen is not in it. If that list is accurate and exhaustive, an unrecognised verb silently resolves to POST, and a guardfile author gets a valid-looking grant that emits the wrong method. That is verifiable by reading method.go and needs no comparison to anything else.

What is lost is the empirical hook. I no longer have an outside observation suggesting the table is incomplete — only the header comment's own wording, which describes the fallthrough explicitly ("unknown verbs such as pin -> POST"). So the confidence drops from "two sources disagree" to "the documentation describes a behaviour that would be a footgun if accurate."

The three dispositions are unchanged, and option 3 — fail closed on an unrecognised verb instead of defaulting to POST — is still the one that matches this grammar's stated philosophy.

## Correction: aosguard is unrelated to ward, and was never evidence here The original body carried a section titled "Evidence the two surfaces may already disagree." It argued that because `aosguard ops forgejo issue reopen` resolves to PATCH, and ward's opcore convention may resolve `reopen` to POST, the two might be projections of the same `[]Descriptor` in disagreement. **That was wrong.** aosguard shares no machinery with ward. It is `aos-v0.195.0` — agentic-os — and its verbs are generated by specgen there. Nothing about how it resolves a method says anything about `http/opcore/method.go`. I have removed the section rather than left it to mislead a reader. How the conflation happened, since it is the kind of thing worth naming: `coilyco-bridge/deploy/AGENTS.md` introduces both in one paragraph as two halves of a single gating story — `ward exec <verb>` for dev commands, `aosguard ops <area>` for operator ones. Both carry "guard" in the name, both expose "verbs", and both are framed as routing commands through a gate. I inferred shared implementation from shared vocabulary and adjacent documentation without checking, while in the same breath flagging that I had not read `method.go`. The version string `aos-v0.195.0` was quoted in my own text and would have settled it. ## What this changes about the issue Less than it might look like, which is why the issue stands rather than being withdrawn. The core claim never depended on aosguard. It is a question about one repository's own documented convention: **the guardfile header lists the verb-to-method table and `reopen` is not in it.** If that list is accurate and exhaustive, an unrecognised verb silently resolves to POST, and a guardfile author gets a valid-looking grant that emits the wrong method. That is verifiable by reading `method.go` and needs no comparison to anything else. What is lost is the empirical hook. I no longer have an outside observation suggesting the table is incomplete — only the header comment's own wording, which describes the fallthrough explicitly (*"unknown verbs such as pin -> POST"*). So the confidence drops from "two sources disagree" to "the documentation describes a behaviour that would be a footgun if accurate." The three dispositions are unchanged, and option 3 — fail closed on an unrecognised verb instead of defaulting to POST — is still the one that matches this grammar's stated philosophy.
Author
Owner

Misfiled — refiled as coilyco-flight-deck/cli-guard#281

This belongs in cli-guard, not here. opcore and its verb-to-method table are cli-guard's; ward-mcp is a consumer that pins them.

The guardfile said so and I read past it:

The body is the FROZEN ward-mcp inline grammar parsed by opcore.ParseInline from ward-mcp's pinned cli-guard

"ward-mcp's pinned cli-guard" names the dependency direction plainly. I took ward-mcp as the owner and filed here, when the sentence says ward-mcp pins something that belongs to another repository.

That is the second attribution error on this one ticket — the first was citing aosguard as evidence about ward, corrected above. Both came from the same habit of inferring ownership from adjacent vocabulary rather than reading the sentence for what it says.

Closing. Nothing in the argument changes; it just needed a different repository. The refiled version at cli-guard#281 carries the corrected confidence statement and drops the withdrawn aosguard comparison.

## Misfiled — refiled as `coilyco-flight-deck/cli-guard#281` This belongs in cli-guard, not here. `opcore` and its verb-to-method table are cli-guard's; ward-mcp is a consumer that pins them. The guardfile said so and I read past it: > The body is the FROZEN ward-mcp inline grammar parsed by `opcore.ParseInline` from **ward-mcp's pinned cli-guard** "ward-mcp's pinned cli-guard" names the dependency direction plainly. I took `ward-mcp` as the owner and filed here, when the sentence says ward-mcp pins something that belongs to another repository. That is the second attribution error on this one ticket — the first was citing aosguard as evidence about ward, corrected above. Both came from the same habit of inferring ownership from adjacent vocabulary rather than reading the sentence for what it says. Closing. Nothing in the argument changes; it just needed a different repository. The refiled version at `cli-guard#281` carries the corrected confidence statement and drops the withdrawn aosguard comparison.
Commenting is not possible because the repository is archived.
No milestone
No project
No assignees
1 participant
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/ward#1656
No description provided.