Deny comments on pinned Forgejo issues, so a pinned issue can only be edited #1364

Open
opened 2026-08-28 22:34:02 +00:00 by coilyco-ops · 3 comments
Owner

Specification from the Portfolio Director seat, on Kai's stated design. Handing the build over rather than scoping how. Building foundational software is boundary-build-foundational-software, which this seat defers.

Outcome

A pinned Forgejo issue cannot receive a comment through either guarded surface. It can only be edited.

Why

coilysiren/inbox#482 decides that observations without an owner stop being issues and move to a maintained document, one pinned issue per repository, edited in place rather than appended to.

The edit-in-place property is the entire value. A two-hundred-comment thread is unorderable, undedupable, and unprunable, so it fails readability before anyone's willingness to read it is even tested. An edited body can be grouped, rewritten, and shortened as understanding improves.

Commenting is the cheap path and agents take cheap paths. Without enforcement the document degrades back into the log it was designed not to be, on a timescale measured in days at current filing rates. The deny is what preserves the property, not decoration on top of it.

Scope: a paired change

Both surfaces, because either one left open defeats the other:

  • AOSguard, the guarded operator surface.
  • The beaver Forgejo MCP guardfile, coilyco-flight-deck/mcp-beaver.

What the refusal should do

  • Refuse a comment on a pinned issue with an error that names the reason and the alternative, so the caller edits rather than retries. A bare denial teaches nothing and the next agent tries again.
  • Leave commenting on unpinned issues untouched. This is not a general restriction on comments.
  • Editing a pinned issue stays permitted.

Two things worth deciding rather than defaulting

  1. Whether pinning is the right trigger, or whether it should key on something explicit. Pinning is convenient because the pin is already the anchor, and it is also load-bearing for other things: #302, #303, and #304 are pinned and are deliberately comment-free by their own update contract, so they benefit. But it means pinning any issue silently changes its comment behaviour. That is a real coupling and it should be a decision rather than a side effect.
  2. What happens to comments already on a pinned issue. Existing ones presumably stay readable. Worth stating so it is not discovered later.

Sequencing, which is Kai's

She sequenced this explicitly: "1 thing at a time." Order recorded in #482 is the pin title first, then this, then standing up the pins. The pins are deliberately not created yet, so nothing is currently broken by this being open.

Done when

  • A comment on a pinned issue is refused through AOSguard and through the beaver MCP, with an error naming the reason and the alternative.
  • A comment on an unpinned issue is unaffected.
  • An edit to a pinned issue is unaffected.
  • Both decisions above are recorded rather than left implicit.

Not in scope

  • Creating the pinned documents. Blocked on the title, which is Kai's and is unsettled.
  • Any change to create_issue labelling. That is coilyco-bridge/deploy#580, which composes with this but is a separate control.

Refs coilysiren/inbox#482, coilyco-bridge/deploy#580, coilyco-flight-deck/mcp-beaver

Specification from the Portfolio Director seat, on Kai's stated design. **Handing the build over rather than scoping how.** Building foundational software is `boundary-build-foundational-software`, which this seat defers. ## Outcome A pinned Forgejo issue **cannot receive a comment** through either guarded surface. It can only be edited. ## Why `coilysiren/inbox#482` decides that observations without an owner stop being issues and move to a maintained document, one pinned issue per repository, **edited in place rather than appended to**. The edit-in-place property is the entire value. A two-hundred-comment thread is unorderable, undedupable, and unprunable, so it fails readability before anyone's willingness to read it is even tested. An edited body can be grouped, rewritten, and shortened as understanding improves. **Commenting is the cheap path and agents take cheap paths.** Without enforcement the document degrades back into the log it was designed not to be, on a timescale measured in days at current filing rates. **The deny is what preserves the property, not decoration on top of it.** ## Scope: a paired change Both surfaces, because either one left open defeats the other: * **AOSguard**, the guarded operator surface. * **The beaver Forgejo MCP guardfile**, `coilyco-flight-deck/mcp-beaver`. ## What the refusal should do * Refuse a comment on a pinned issue with an error that **names the reason and the alternative**, so the caller edits rather than retries. A bare denial teaches nothing and the next agent tries again. * Leave commenting on unpinned issues untouched. This is not a general restriction on comments. * Editing a pinned issue stays permitted. ## Two things worth deciding rather than defaulting 1. **Whether pinning is the right trigger, or whether it should key on something explicit.** Pinning is convenient because the pin is already the anchor, and it is also load-bearing for other things: `#302`, `#303`, and `#304` are pinned and are deliberately comment-free by their own update contract, so they benefit. But it means pinning any issue silently changes its comment behaviour. That is a real coupling and it should be a decision rather than a side effect. 2. **What happens to comments already on a pinned issue.** Existing ones presumably stay readable. Worth stating so it is not discovered later. ## Sequencing, which is Kai's She sequenced this explicitly: **"1 thing at a time."** Order recorded in `#482` is the pin title first, then this, then standing up the pins. **The pins are deliberately not created yet**, so nothing is currently broken by this being open. ## Done when * A comment on a pinned issue is refused through AOSguard and through the beaver MCP, with an error naming the reason and the alternative. * A comment on an unpinned issue is unaffected. * An edit to a pinned issue is unaffected. * Both decisions above are recorded rather than left implicit. ## Not in scope * Creating the pinned documents. Blocked on the title, which is Kai's and is unsettled. * Any change to `create_issue` labelling. That is `coilyco-bridge/deploy#580`, which composes with this but is a separate control. Refs `coilysiren/inbox#482`, `coilyco-bridge/deploy#580`, `coilyco-flight-deck/mcp-beaver`
Author
Owner

Decision 1 is settled, and not by preference. The trigger is a reserved label, not the pin.

Portia (director seat), 2026-08-29. Raised by Vera (sysadmin seat) hitting the wall mid-pass. Verified here rather than relayed.

The capability fact

Forgejo gates pin management on repository admin. coilyco-ops holds it on exactly one repository:

  • coilysiren/inbox - "permissions": {"admin": true, ...}
  • coilyco-flight-deck/infrastructure - "permissions": {"admin": false, "pull": true, "push": true}
  • coilyco-flight-deck/agent-proxy - "permissions": {"admin": false, "pull": true, "push": true}

So one pinned document per repository is not executable anywhere except inbox, and it never was. #482 and this issue both specify an anchor the credential cannot create.

This is not a guardfile change. That was the first read and it is wrong. Widening aosguard ops forgejo issue pin past its fixed target would surface a 403 rather than a pin. Making pins work fleet-wide requires granting coilyco-ops repository admin on roughly thirty repositories, which is a large standing permission expansion bought for a cosmetic anchor. I am not routing that to the platform seat, because there is nothing there to build, and it is Kai's grant rather than anyone's implementation.

The call

The deny keys on a reserved label. Proposal: state/ambient, org-level, at most one issue per repository.

  • coilyco-ops already holds push, which carries label writes. Verified today rather than assumed: I set labels on agent-proxy#151 and deploy#819 this session.
  • It resolves decision 1 as this issue framed it. The coupling worry there was real and is now moot: pinning an unrelated issue no longer silently changes its comment behaviour, because pinning is no longer the trigger.
  • It makes the deny actually reachable on every repository, which the pin-keyed version never could be.

Pin where possible, label everywhere, deny on the label. Inbox keeps its pins, so #302, #303, and #304 are unaffected and keep their comment-free contract. If they should also gain the deny, label them too rather than special-casing the pin.

Decision 2 is unchanged: comments already present stay readable. State it in the implementation.

What this does not change

#1364 still lands before any document is created. Kai's "1 thing at a time" stands, and the sequencing in #482 is otherwise intact. This changes what the deny keys on, not when it ships.

The scope stays paired: AOSguard and the beaver Forgejo MCP guardfile, both.

Why not create the documents unpinned in the meantime

Asked, and the answer is no. The deny keys on a marker. An unpinned, unlabelled document carries no marker, so it would receive no protection even after this lands, and would be a comment log from birth. That is exactly the degradation #482 exists to prevent, so the interim option is worse than waiting rather than a partial step toward it.

**Decision 1 is settled, and not by preference. The trigger is a reserved label, not the pin.** Portia (director seat), 2026-08-29. Raised by Vera (sysadmin seat) hitting the wall mid-pass. Verified here rather than relayed. ## The capability fact Forgejo gates pin management on repository admin. `coilyco-ops` holds it on exactly one repository: * `coilysiren/inbox` - `"permissions": {"admin": true, ...}` * `coilyco-flight-deck/infrastructure` - `"permissions": {"admin": false, "pull": true, "push": true}` * `coilyco-flight-deck/agent-proxy` - `"permissions": {"admin": false, "pull": true, "push": true}` So **one pinned document per repository is not executable anywhere except inbox**, and it never was. `#482` and this issue both specify an anchor the credential cannot create. **This is not a guardfile change.** That was the first read and it is wrong. Widening `aosguard ops forgejo issue pin` past its fixed target would surface a 403 rather than a pin. Making pins work fleet-wide requires granting `coilyco-ops` **repository admin on roughly thirty repositories**, which is a large standing permission expansion bought for a cosmetic anchor. I am not routing that to the platform seat, because there is nothing there to build, and it is Kai's grant rather than anyone's implementation. ## The call **The deny keys on a reserved label.** Proposal: `state/ambient`, org-level, at most one issue per repository. * `coilyco-ops` already holds `push`, which carries label writes. Verified today rather than assumed: I set labels on `agent-proxy#151` and `deploy#819` this session. * It resolves **decision 1** as this issue framed it. The coupling worry there was real and is now moot: pinning an unrelated issue no longer silently changes its comment behaviour, because pinning is no longer the trigger. * It makes the deny actually reachable on every repository, which the pin-keyed version never could be. **Pin where possible, label everywhere, deny on the label.** Inbox keeps its pins, so `#302`, `#303`, and `#304` are unaffected and keep their comment-free contract. If they should also gain the deny, label them too rather than special-casing the pin. **Decision 2 is unchanged**: comments already present stay readable. State it in the implementation. ## What this does not change `#1364` still lands before any document is created. Kai's "1 thing at a time" stands, and the sequencing in `#482` is otherwise intact. This changes what the deny keys on, not when it ships. The scope stays paired: AOSguard and the beaver Forgejo MCP guardfile, both. ## Why not create the documents unpinned in the meantime Asked, and the answer is no. The deny keys on a marker. An unpinned, unlabelled document carries no marker, so it would receive no protection even after this lands, and would be a comment log from birth. That is exactly the degradation `#482` exists to prevent, so the interim option is worse than waiting rather than a partial step toward it.
Author
Owner

Kai granted the admin. Half my comment above is now wrong, and decision 1 lands differently: the pin is the anchor, the label is the trigger.

Portia (director seat), 2026-08-29, about an hour after the comment above.

What changed

coilyco-ops now holds repository admin across the three orgs. Verified:

  • coilyco-flight-deck/infrastructure - admin: true
  • coilyco-bridge/deploy - admin: true
  • coilyco-gaming/eco-app - admin: true

All three read admin: false when I wrote the comment above. Kai overturned the fork I recorded for her, which was hers to overturn.

Two corrections to my own comment

  1. "Real pins fleet-wide means granting repository admin" - accurate, and it has now happened. The capability argument for dropping the pin is gone.
  2. "This is NOT a guardfile change and not platform's to build" - true when written, false now. The credential is no longer the constraint, so the fixed target in the verb definition is, and that is precisely a guardfile change. Filed at #1375, scoped to the pin verbs and nothing else. Left standing, that sentence would have been a coilysiren/inbox#484 form-4 claim sitting in the issue that extended #484.

Decision 1, settled properly this time

The anchor is the pin. The trigger is the label. They are separable and each does the job it is good at.

  • Pin - discoverability. It sorts the document to the top of the issue list, which is what inbox#482 asked for and what a reader actually needs. A label does not do this.
  • state/ambient label - what the deny keys on.

This is the answer to decision 1 exactly as this issue framed it: "whether pinning is the right trigger, or whether it should key on something explicit." Something explicit. The coupling worry stands on its own merits and never depended on the credential: keying the deny on the pin means pinning any issue silently changes its comment behaviour, and that should be a decision rather than a side effect. The admin grant does not touch that argument.

So the label survives, and it survives as the trigger rather than as a substitute for the pin.

#302, #303, and #304 are unaffected either way. They are already pinned and already comment-free by their own update contract. If they should also gain enforcement, label them, which is now an explicit act rather than something their pin does to them silently.

Decision 2 unchanged: existing comments stay readable, and say so in the implementation.

Sequencing, still unchanged

This issue still lands before any document is created. Kai's "1 thing at a time" holds, and the grant does not reorder anything. #1375 is needed at step 3, when documents are stood up, so it rides with that step rather than jumping ahead of this one.

Still no documents before this deny lands, for the reason that never depended on pins: a document without its marker gets no protection even after the deny ships.

**Kai granted the admin. Half my comment above is now wrong, and decision 1 lands differently: the pin is the anchor, the label is the trigger.** Portia (director seat), 2026-08-29, about an hour after the comment above. ## What changed `coilyco-ops` now holds repository admin across the three orgs. Verified: * `coilyco-flight-deck/infrastructure` - `admin: true` * `coilyco-bridge/deploy` - `admin: true` * `coilyco-gaming/eco-app` - `admin: true` All three read `admin: false` when I wrote the comment above. Kai overturned the fork I recorded for her, which was hers to overturn. ## Two corrections to my own comment 1. **"Real pins fleet-wide means granting repository admin"** - accurate, and it has now happened. The capability argument for dropping the pin is gone. 2. **"This is NOT a guardfile change and not platform's to build"** - true when written, false now. The credential is no longer the constraint, so the fixed target in the verb definition is, and that is precisely a guardfile change. Filed at **#1375**, scoped to the pin verbs and nothing else. Left standing, that sentence would have been a `coilysiren/inbox#484` form-4 claim sitting in the issue that extended `#484`. ## Decision 1, settled properly this time **The anchor is the pin. The trigger is the label.** They are separable and each does the job it is good at. * **Pin** - discoverability. It sorts the document to the top of the issue list, which is what `inbox#482` asked for and what a reader actually needs. A label does not do this. * **`state/ambient` label** - what the deny keys on. This is the answer to decision 1 exactly as this issue framed it: *"whether pinning is the right trigger, or whether it should key on something explicit."* **Something explicit.** The coupling worry stands on its own merits and never depended on the credential: keying the deny on the pin means pinning any issue silently changes its comment behaviour, and that should be a decision rather than a side effect. The admin grant does not touch that argument. So the label survives, and it survives as the trigger rather than as a substitute for the pin. `#302`, `#303`, and `#304` are unaffected either way. They are already pinned and already comment-free by their own update contract. If they should also gain enforcement, label them, which is now an explicit act rather than something their pin does to them silently. **Decision 2 unchanged**: existing comments stay readable, and say so in the implementation. ## Sequencing, still unchanged This issue still lands before any document is created. Kai's "1 thing at a time" holds, and the grant does not reorder anything. **#1375 is needed at step 3**, when documents are stood up, so it rides with that step rather than jumping ahead of this one. Still no documents before this deny lands, for the reason that never depended on pins: a document without its marker gets no protection even after the deny ships.
Author
Owner

Two spec additions, both settled here rather than left to the implementer. The deny keys on the state/ prefix, and the label is created --exclusive.

Portia (director seat), 2026-08-29. The sysadmin seat put the exclusivity question to Kai; I am taking it instead, because the evidence that settles it is already in and the reversal cost is at its minimum right now.

The deny keys on state/, not on state/ambient

I read the existing pins on coilysiren/inbox rather than reasoning about them. There are three, and they are all state projections:

  • #302 STATE: current campaign
  • #303 STATE: product portfolio
  • #304 STATE: platform and operations

All three already carry the update contract this deny exists to enforce, in their own words: "This body is the current projection and may be rewritten in place. Keep this issue comment-free." They have been running that contract on discipline alone.

So state/ is a namespace with four plausible members on day one, not a lone marker: state/campaign, state/portfolio, state/platform, state/ambient. Whatever those three eventually get called, state/ambient is not going to be the only one.

Therefore: key the refusal on any label matching state/, not on the literal string state/ambient. Keying on the exact label means the second state document type silently loses protection the day someone adds it, and it loses it quietly, which is coilysiren/inbox#484's whole subject. A prefix match costs nothing now and cannot fail that way later.

This also gives #302, #303, and #304 a route to real enforcement: label them, and the contract they currently keep by discipline becomes a refusal. That stays an explicit act per issue rather than something their pin does to them, which was the coupling this issue flagged.

The label is created --exclusive

House convention already distinguishes the two cases, and it is checkable rather than a matter of taste:

  • priority/* and autonomy/* are "exclusive": true. An issue cannot be two priorities.
  • role/* is "exclusive": false. coilyco-bridge/deploy#818 carries role/human and role/sysadmin, legitimately.

The discriminator is whether one issue can hold two values at once. An issue cannot be two state projections. Each of #302, #303, #304 is exactly one kind, and the ambient document will be exactly one kind. That is the priority/* shape, not the role/* shape.

On the deferral. The stated reason to wait was that exclusivity may not be editable after creation, so a wrong pick needs delete-and-recreate, which strips the label from every issue carrying it. That is correct and it is precisely why this should be decided now: zero issues carry the label today, so the reversal cost is zero and rises monotonically from here. An option whose cost of being wrong grows with time is the one case where deferring is strictly worse than choosing.

Kai can overturn it while it is still free to overturn.

Unchanged

Everything else in this issue stands. Paired change across AOSguard and the beaver MCP guardfile, refusal names the reason and the alternative, unpinned issues unaffected, editing unaffected, existing comments stay readable.

The label itself is an inert prerequisite and is not gated by this issue: creating it produces no document, changes no comment behaviour, and is invisible until something carries it. It can land whenever, and does not wait on a platform seat.

**Two spec additions, both settled here rather than left to the implementer. The deny keys on the `state/` prefix, and the label is created `--exclusive`.** Portia (director seat), 2026-08-29. The sysadmin seat put the exclusivity question to Kai; I am taking it instead, because the evidence that settles it is already in and the reversal cost is at its minimum right now. ## The deny keys on `state/`, not on `state/ambient` I read the existing pins on `coilysiren/inbox` rather than reasoning about them. There are three, and they are all state projections: * `#302` **STATE: current campaign** * `#303` **STATE: product portfolio** * `#304` **STATE: platform and operations** All three already carry the update contract this deny exists to enforce, in their own words: *"This body is the current projection and may be rewritten in place. Keep this issue comment-free."* They have been running that contract on discipline alone. So `state/` is a namespace with **four plausible members on day one**, not a lone marker: `state/campaign`, `state/portfolio`, `state/platform`, `state/ambient`. Whatever those three eventually get called, `state/ambient` is not going to be the only one. **Therefore: key the refusal on any label matching `state/`, not on the literal string `state/ambient`.** Keying on the exact label means the second state document type silently loses protection the day someone adds it, and it loses it quietly, which is `coilysiren/inbox#484`'s whole subject. A prefix match costs nothing now and cannot fail that way later. This also gives `#302`, `#303`, and `#304` a route to real enforcement: label them, and the contract they currently keep by discipline becomes a refusal. That stays an explicit act per issue rather than something their pin does to them, which was the coupling this issue flagged. ## The label is created `--exclusive` House convention already distinguishes the two cases, and it is checkable rather than a matter of taste: * `priority/*` and `autonomy/*` are `"exclusive": true`. An issue cannot be two priorities. * `role/*` is `"exclusive": false`. `coilyco-bridge/deploy#818` carries `role/human` **and** `role/sysadmin`, legitimately. The discriminator is whether one issue can hold two values at once. **An issue cannot be two state projections.** Each of `#302`, `#303`, `#304` is exactly one kind, and the ambient document will be exactly one kind. That is the `priority/*` shape, not the `role/*` shape. **On the deferral.** The stated reason to wait was that exclusivity may not be editable after creation, so a wrong pick needs delete-and-recreate, which strips the label from every issue carrying it. That is correct and it is precisely why this should be decided now: **zero issues carry the label today**, so the reversal cost is zero and rises monotonically from here. An option whose cost of being wrong grows with time is the one case where deferring is strictly worse than choosing. Kai can overturn it while it is still free to overturn. ## Unchanged Everything else in this issue stands. Paired change across AOSguard and the beaver MCP guardfile, refusal names the reason and the alternative, unpinned issues unaffected, editing unaffected, existing comments stay readable. The label itself is an **inert prerequisite** and is not gated by this issue: creating it produces no document, changes no comment behaviour, and is invisible until something carries it. It can land whenever, and does not wait on a platform seat.
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/agentic-os#1364
No description provided.