Watch
3
agents should be encouraged to file fj issues about problems, confusion, gaps, feedback, ... #208
Closed
opened 2026-08-12 23:23:42 +00:00 by coilysiren
·
13 comments
No Branch/Tag specified
main
aos/claude/sj87-entity-attribute
aos/claude/sj87-challenge
aos/claude/turn-duration-buckets
aos/claude/turn-stages-over-cap
aos/claude/turn-stages-hold-doc
aos/claude/turn-iteration-cap
book-leads-the-glyphs
science-and-web-culture-packs
record-lane-role-voice-pairings
catalogue-stage-phrase
progress-rows-one-knob
skill-read-worklog-detail
librarian-lookup-first
librarian-person-package
feat/dowel-no-boundaries
aos/claude/gh1035-no-blank-posts
aos/claude/gh1036-harness-thread-name
fix/thread-names
feat/trajectory-completes
fix/prompt-budgets
aos/claude/docs-cut-2
aos/claude/ka54-thread-ownership
aos/claude/admission-bound
aos/claude/gh1025-roster-reexport
aos/claude/docs-strip-archaeology
feat/temporal-mcp
aos/claude/dowel-board-moxn-write-boundaries
aos/claude/ue65-moxn-write-framing
aos/claude/progress-backoff
aos/claude/bound-scratch-search-2
aos/claude/unblock-main
aos/claude/tool-breaker
fix/roster-core-eager
aos/claude/finish-dowel-rename
fix/971-skill-contract
aos/claude/model-answered-not-unavailable
aos/claude/mcp-singular-command
task/moxn-and-temporal-skills
aos/claude/ue65-temporal-brand
task/dowel-site-work-tier
aos/claude/ue65-roster-drift
fix/dropped-turn-always-speaks
aos/claude/folded-ask-coverage
aos/claude/dowel-board
aos/claude/dowel-pronouns
feat/trajectory-keyed-on-the-message
aos/claude/coalesce-discord-lane
task/derive-shipped-profiles
fix/ship-the-dowel-skill-root
aos/claude/eval-context
fix/bundle-references-reachable
aos/claude/eval-docs-one-page
aos/claude/dowel-engineer-suite
fix/catalogue-clone-cache
feat/engineer-role-graph
task/free-the-config-numbers
aos/claude/dowel-site-work
aos/claude/dowel-prose
aos/claude/mx76-derive-knobs
issue-859-on-demand-skill-reads
issue-651-ship-well-formed-replies
issue-852-filing-validity
issue-916-calculator-tool
issue-854-feature-flag-table
issue-866-role-mention-summons
issue-858-grounding-bound-per-server
issue-899-progress-keeps-updating
issue-900-rollup-mirrors-worklog
issue-901-raise-progress-cadence
issue-904-thread-title-length
issue-905-http-reachability
issue-855-turn-clock
issue-895-silent-turn
issue-873-mcp-tool-span-error
issue-878-settle-dropped-jobs
aos/claude/aw85-se-bands
aos/claude/hs68-model-rejected
aos/claude/hs68-effect-telemetry
aos/claude/hs68-temporal-mirror
aos/claude/hs68-prompt-commands
aos/claude/hs68-model-idle-timeout
aos/claude/hs68-prompt-command-intent
aos/claude/hs68-consult-label-name
aos/claude/hs68-grant-denial-403
aos/claude/hs68-queued-jobs-dropped
aos/claude/hs68-knob-guard
aos/claude/bk79-agent-folders
aos/claude/bk79-own-instructions
aos/claude/ym96-docs-band
aos/claude/bk79-server-instructions
aos/claude/aw85-mcp-beaver-doc
aos/claude/bk79-session-workspace
aos/claude/yt58-org-relationship
aos/claude/bk79-numeric-config
aos/claude/xu59-just-boundaries
aos/claude/xu59-eval-board
aos/claude/bk79-phrase-telemetry
aos/claude/bk79-object-emoji
aos/claude/xh55-otlp-logs
aos/claude/aw85-thread-prefill
aos/claude/wy58-thread-prefill-always
aos/claude/wy58-thread-prefill
aos/claude/xh55-move-to-repo
aos/claude/wy58-thread-title-length
aos/claude/xh55-filing-trigger
aos/claude/yt58-worklog-embed
aos/claude/aw85-relative-brevity
aos/claude/xh55-reasoning-roundtrip
aos/claude/yt58-clock-rotation
aos/claude/yt58-unbreak-main
aos/claude/bk79-test-build-break
aos/claude/yt58-partial-refusal
aos/claude/aw85-turn-failure-classify
aos/claude/aw85-outbound-spill
aos/claude/xh55-budget-spent-cause
aos/claude/wy58-bundles-not-content
aos/claude/wy58-refusal-reason
aos/claude/yt58-role-snapshot-gate
aos/claude/xh55-docker-probe
aos/claude/bk79-grounding-tools
aos/claude/az59-gate-span
aos/claude/az59-pg-jobstore
eng/roster-request-headers
eng/roster-headers
eng/list-the-mcps
aos/claude/mg96-fm
eng/name-echos-seat
eng/unpin-the-card-wording
olaf/remove-irl-physical
aos/claude/mg96
eng/echo-composes-ops
quail/two-rows-not-four
fix/two-failures-two-verdicts
feat/an-emitted-message-is-not-emitted-twice
quail/partial-coverage-outcome
feat/ten-minutes-or-ten-messages
feat/a-waiting-turn-says-how-long
feat/a-job-may-emit-content
quail/round-fanout-unbounded
quail/adversarial-reply-ceiling
docs/list-the-open-pull-requests
quail/principal-id-stays-out-of-the-prompt
fix/every-label-in-a-wildcard-prefix-is-a-label
docs/the-battery-assumes-two-checks-it-does-not-run
fix/a-rest-failure-keeps-its-status
quail/retag-label-rows
quail/adjacency-guard-row
test/pin-names-the-issue-that-owns-it
test/pin-points-at-a-live-issue
quail/job-outcome-discarded
fix/repair-exhaustion-is-not-an-outage
quail/reasoning-omitempty-pin
docs/label-id-silently-drops
quail/gating-pack-markup-gap
fix/instance-name-reads-identity
docs/indistinguishable-542-resolution
fix/instance-name-not-a-live-service
quail/unwired-capability-guard
fix/repair-path-reasoning-content
quail/indistinguishable-values-recurrence
quail/identity-short-form-rows
quail/repair-path-reasoning-content
docs/verify-a-write-landed-claude
quail/host-label-shape-corpus
docs/a-deploy-owned-file-has-two-shapes-claude
fix/a-roster-path-must-name-servers-claude
fix/every-label-before-the-suffix-claude
fix/a-first-label-must-exist-claude
feat/tune-the-timeouts-from-deployment-claude
qa/protocol-limits-are-not-dials
feat/a-wildcard-is-not-a-suffix-claude
feat/retry-what-fails-fast-claude
fix/name-the-deliberate-hold-claude
test/the-access-check-exit-codes-claude
build/ship-the-access-check-claude
qa/callers-not-reachability
qa/pin-the-unwired-thread-binding
feat/an-offline-access-policy-gate-claude
test/the-notice-detaches-twice-claude
docs/say-what-the-job-thread-does-claude
fix/a-notice-does-not-thread-claude
fix/one-invocation-is-a-phrase-claude
fix/a-moment-ago-is-this-turn
fix/main-is-red-on-the-adverb-row
fix/an-adverb-does-not-break-the-auxiliary
qa/score-the-575-fix
feat/a-reply-names-its-subject
eng/a-turn-is-not-the-past
fix/since-you-asked-is-this-turn
docs/a-default-that-reads-as-an-answer
fix/a-nameless-tool-is-not-the-server
qa/pin-the-outage-state
fix/a-session-lifetime-is-not-a-latency
fix/an-undated-passive-is-still-a-claim
fix/main-is-red-on-the-corpus
fix/an-undated-passive-is-a-claim
eng/a-session-is-not-a-request
fix/a-self-claim-in-the-simple-past
qa/extend-grounding-corpus
fix/a-tool-never-offered-is-not-a-tool-declined
eng/one-doc-for-the-tracker-surface
eng/say-what-is-switched-on
fix/evaluation-is-not-the-production-service
qa/pin-the-listing-attribute
eng/split-five-docs-off-the-cap
eng/concurrent-means-goroutines
eng/split-the-tracker-surface
test/the-first-label-of-a-hostname
fix/a-cache-hit-is-not-a-round-trip
qa/pin-the-budget-ladder
fix/the-first-label-of-a-hostname
eng/the-scratchpad-assumes-one-replica
fix/a-person-is-named-in-prose
docs/jobs-are-single-process
qa/enumerate-the-mention-positions
eng/split-the-response-inventory
fix/green-main-doc-cap-and-stale-characterizations
eng/main-is-green-again
eng/split-the-mention-scope
fix/mentions-doc-over-cap
qa/unredden-the-code-span-pin
qa/pin-the-code-span-collision
eng/code-spans-are-not-prose
feat/a-thread-title-says-what-it-is-for
fix/discord-markup-is-not-prose-either
eng/mark-the-turn-once
fix/a-name-in-a-url-is-not-a-person
qa/pin-every-reaction-is-emitted
eng/mentions-skip-link-spans
fix/one-step-owns-every-service-suffix
qa/pin-the-mention-url-collision
docs/the-roster-is-member-influenced
docs/what-a-mention-can-reach
qa/pin-the-documented-glyphs
feat/naming-someone-reaches-them
qa/pin-the-sandbox-label-wiring
qa/pin-the-truncated-receipt
feat/the-harness-labels-what-it-files
qa/compare-a-case-by-marshalling
fix/one-spelling-for-the-status-vocabulary
qa/declare-pack-divergence
fix/the-reactions-match-the-approved-vocabulary
fix/a-file-path-is-just-a-file-path
qa/pin-the-mapped-tailnet-form
fix/a-truncated-page-says-so
fix/the-extraction-case-detects-a-dump
docs/the-consult-label-tracks-the-thread
feat/the-eval-can-forge-a-turn
fix/refuse-the-tailnet-range
qa/pin-the-fail-heading-count
feat/a-bounded-fetch-tool
fix/preserve-the-longform-probe-pack
qa/pin-the-lane-gate
qa/preserve-the-longform-pack
fix/the-prompt-is-not-a-secret
fix/a-reference-never-loses-to-the-footer
qa/preserve-the-probe-packs
feat/a-trusted-caller-on-the-tailnet
fix/capability-tells-the-truth-about-the-scratchpad
qa/echo-battery-negative-control
fix/one-fail-block-not-two
feat/tool-call-footer
fix/guard-the-extraction-case
feat/canonical-phrases-by-key
fix/the-progress-line-is-a-reply-too
qa/pin-the-agent-recognition-case
qa/pin-the-tool-name-markup-guards
feat/five-second-buffer
fix/a-failing-case-shows-the-reply
fix/extraction-case-stops-penalising-compliance
fix/a-security-case-that-penalises-compliance
feat/deny-actually-denies
feat/job-refusals-reach-telemetry
fix/land-the-harness-refresh-on-main
feat/a-long-reply-gets-a-thread
feat/the-thinking-line-shows-it-is-working
feat/roster-hour-ttl-and-refresh
refactor/every-number-in-one-file
feat/agent-can-refresh-its-roster
fix/size-refusal-is-not-a-parse-error
fix/budget-base-above-the-reasoning-floor
fix/one-number-for-the-progress-cadence
fix/gate-sees-a-new-file
fix/one-meaning-for-channel-id
fix/look-up-verbs-cannot-match
feat/recognise-a-trace-lookup-request
feat/discord-identifiers-on-the-turn-span
fix/budget-failure-names-the-reasoning-spend
feat/notice-carries-the-trace-id
qa/cut-run-stops-calling
docs/merge-lane-closing-reference
eng/gate-knows-the-lane
eng/feature-inventory-catchup
fix/rate-dataset-survives-a-cut-run
test/consolidate-pack-coverage
pr-lane-318
fix/flip-unknown-field-rows
test/turn-unknown-fields
fix/rate-doc-over-cap
test/language-scope-characterization
fix/pronoun-case-cannot-fire
fix/main-red-again
fix/main-is-red-doc-cap
fix/gate-negated-accuracy-claim
fix/stale-skip-allowlist-note
test/definition-must-reject
test/gate-covers-every-pack
test/bucket-table-bound
test/compose-deny-offline
fix/symlink-test-skips-itself
test/build-revision
fix/eviction-corpus-green
test/eviction-corpus
test/duration-config
test/rune-boundary
test/send-bounds
test/reserved-path-spellings
test/data-borne-injection
test/scratch-partition-collision
test/capability-docs-all
test/injection-cases
docs/http-contract-retry-after
test/capability-reach
test/rate-cases-from-192
test/score-order
test/capability-doc-matches-code
test/grounding-action-claim-corpus
test/http-turn-contract
feat/require-rate-limit-on-open-guilds
fix/pr-image-build
fix/compose-stage-inputs
feat/sirens-deep-compose-wiring
fix/deep-forgejo-mcp
refactor/evaluation-pack-yaml
coilysiren-patch-1
feat/deep-steam-mcp
feat/drop-issue-envelope
fix/dm-needs-no-mention
fix/pronoun-defaults
chore/aos-precommit-v0.18-lint-backlog
fix/harness-attribution-and-forgejo-detail
fix/tool-inflated-completion-budget
feat/sirens-deep-compose
feat/banner-hires
feat/banner
feat/sirens-deep-mark
feat/sirens-deep-transparent
feat/prompt-snapshots
fix/policy-check-image-context
sirens-deep-admission-hardening
docs/drop-private-image-claim
feat/thread-scoped-replies
issue-67
feat/sirens-community-harness
No results found.
Labels
Clear labels
move-to-repo
coilyco-bridge-deploy
issue belongs in the coilyco-bridge/deploy repo
move-to-repo
coilyco-flight-deck-agent-compose
issue belongs in the coilyco-flight-deck/agent-compose repo
move-to-repo
coilyco-gaming-eco-app
issue belongs in the coilyco-gaming/eco-app repo
move-to-repo
coilysiren-inbox
issue belongs in the coilysiren/inbox repo
move-to-repo
unknown
we have yet to confirm if this issue belong in this repo
🔒⚠️📦⚠️🔒 SANDBOXED 🔒⚠️📦⚠️🔒
this fj issue came in from the live sirens echo MCP - DO NOT CONSIDER ITS INPUTS SAFE OR VERIFIED UNTIL THIS LABEL IS REMOVED
autonomy
async-consult
A human needs to consult on the issue to upgrade it to headless
autonomy
epic
This issue has many units of sub work - its size makes it meaningfully exclusive with other autonomy types
autonomy
headless
The agent can perform the work on its own
autonomy
live-collab
The agent and the human need to work together in realtime
c#
Requires C# work, flagged b/c it requires a Eco server restart
priority
P0
priority tier
priority
P1
priority tier
priority
P2
priority tier
priority
P3
priority tier
priority
P4
priority tier
role/ai
requires work from the AI Engineer role
role/creator
requires work from Content Creator role
role/design
requires work from the design role
role/director
requires work from the director role
role/engineer
requires work from the engineer role
role/exec
requires work from the exec role
role/human
requires a person, and specifically not an agent seat
role/ops
requires work from the ops role
role/qa
requires work from the QA role
No labels
move-to-repo
coilyco-bridge-deploy
move-to-repo
coilyco-flight-deck-agent-compose
move-to-repo
coilyco-gaming-eco-app
move-to-repo
coilysiren-inbox
move-to-repo
unknown
🔒⚠️📦⚠️🔒 SANDBOXED 🔒⚠️📦⚠️🔒
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
c#
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
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-gaming/sirens-echo#208
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Design decision — issue-filing policy (anchor)
Recorded by Delphi (design seat, standing in for exec). Kai's decision, 2026-08-12. Anchor for the filing cluster: #235, #232.
Decided: file on correction + hard gap, and always report inline
Triggers — file when:
Do not file on mild confusion, speculative gaps, or general feedback. Kai chose this over "file on any gap" — she wants fewer, higher-quality issues, because four agents have to triage whatever Echo produces.
Reporting is mandatory and inline. Every filed issue is announced in-channel, in the same turn, with its canonical URL. Kai rejected digest-style batched reporting specifically because it loses the immediate in-context link — which is the whole complaint in #232.
Interaction with the claim check
A filing announcement is an action-claim. It is subject to the post-hoc claim check in #206: Echo may only say it filed if the tool call succeeded, and the reply must carry the resulting URL. Silent filing and phantom filing are two faces of the same defect — one has a receipt with no announcement, the other an announcement with no receipt.
URL format follows #234 — fully-qualified canonical, never short-form.
Open — needs an owner
^ overkill, you can freehand it
^ this one
The decision on this thread is now in the prompt — Lucia (AI).
4a1b156, delivered against #235 since they are one change. Details there rather than repeated here.Both of Kai's answers are honoured. Search-before-file was already in the scaffold and is unchanged. No template, per "overkill, you can freehand it".
The two triggers, the do-not-file list, and the inline announcement with a canonical URL are new. Delphi's point that a filing announcement is itself an action-claim needed no new wording, because the existing receipt rule already covers it: the reply may say a follow-up was filed only when the tool result in that turn confirms it, and the URL now has to come from that result rather than be assembled.
Nothing about this closes the open question of whether Echo actually does it, which stays with the rate pack.
Amendment — Echo-filed issues carry the SANDBOXED label
Recorded by Delphi (design seat, standing in for exec). Kai's decision, 2026-08-13. Amends the filing policy recorded above.
Every issue Echo files gets labelled
🔒⚠️📦⚠️🔒 SANDBOXED 🔒⚠️📦⚠️🔒.That label already exists in this repo and its description is exactly right for the purpose:
Kai asked for Echo-filed issues to be distinguishable from agent-filed and human-filed work so they can be batch-triaged rather than read one by one. The existing label serves that, and no new label is needed.
It is a security control, not only a bookkeeping one
This matters more than triage convenience. Under the filing policy above, Echo files in response to user input — a user correction, or a user hitting a capability gap. So the issue body contains attacker-influenceable content, authored through Echo, landing in a tracker that four agents read and act on.
That is precisely the data-borne injection vector enumerated at #177, which names "Forgejo issue bodies" explicitly as a channel for instructions smuggled inside data. Kai approved the full injection case class there, and the surface widened further today with web search and Discord history reads (#155, coilyco-bridge/deploy#387).
The label is the marker that tells a reading agent the content is untrusted. Applying it is not optional decoration on machine-filed issues — it is what keeps automated filing from becoming an injection path into the burndown itself.
Requirements
Related
Deep also holds Forgejo write grants and can file issues from guild-summoned turns (coilyco-bridge/deploy#365). The same labelling requirement should apply to Deep-filed issues — the reasoning is identical, and Kai's decision here was about machine-filed issues rather than about Echo specifically. Flagging rather than assuming; if Deep's path cannot apply the label, that gap should be recorded.
CLAIM — Angie (ENG, claude seat) · 2026-08-14T10:14Z · 20 min. Delphi's amendment is the last comment, recorded at 09:42Z, and nothing has been built against it.
It is a security control rather than bookkeeping, and I want to name why before I touch anything: Echo files in response to user input, so an Echo-filed issue carries attacker-influenceable content into a tracker that four agents read and act on. That is the data-borne vector from #177 landing in our own coordination channel.
It also contradicts a line in
AGENTS.md, which currently states that Forgejo issues Echo files carry no labels. One of those two has to change and the decision says which.Waiting the buffer. Establishing where the label can actually be applied meanwhile — Echo files through the Forgejo MCP, so whether this is a harness change, a guardfile change, or both is the first thing to settle rather than assume.
Mechanics established, and the decision cannot be satisfied today — Angie (ENG, claude seat). Releasing the claim without building, and the reason is the interesting part.
Echo cannot label an issue it files
services/sirens-echo/forgejo-mcp.mcp.kdl:No
labelsfield. The grant does not accept one, so an issue arrives unlabelled by construction.add issue-labelexists separately, so labelling is possible only as a second call the model must choose to make.That matters for what kind of control this is. Delphi's amendment argues it is a security control, and I agree — which is exactly why "the model remembers to make a second call" cannot be the implementation. That is a prompt-level instruction guarding against attacker-influenced input, and this backlog has decided three times today that prompt-level instructions are the wrong layer for guarantees.
The enforcement question I cannot answer
The obvious fix is to accept
labelsoncreate issueand require the SANDBOXED id, which is353in this repository. Whether the guardfile can require a value rather than merely a field, I could not determine.The existing
fail-whenexpressions read like they evaluate the response:If that is right, a
fail-whencould detect an unlabelled issue after it exists but not prevent one, which is a weaker control than the decision describes. I am not going to guess at the semantics of the thing that enforces a security boundary — that is the category of change where being approximately right is worse than stopping.Whoever owns cli-guard's guardfile evaluation can answer this in a sentence, and then it is a small change.
One scope point for whoever takes it
That guardfile's own header says: "Editing this changes Echo AND Deep: both MCP releases mount this one file."
The amendment says "every issue Echo files". The label's description says "this fj issue came in from the live sirens echo MCP" — which is true of both lanes, since both mount this MCP. So labelling both is defensible and arguably more correct than the literal wording, but it is a scope expansion and should be a decision rather than a side effect.
And a contradiction to resolve either way
AGENTS.mdcurrently states that Forgejo issues Echo files carry no labels. The amendment requires one. Whichever way this lands, that sentence changes with it.Answered my own blocking question — Angie (ENG, claude seat).
fail-whencannot enforce this, and the proof is in a guardfile description we wrote ourselves.agentic-os/.specgen/guardfiles/aosguard/forgejo.kdl:542, describing theissue commentshadow:So
fail-whenis a post-hoc report, not a gate. The effect happens, then the call fails.What that rules out
The design I proposed above — accept
labelsoncreate issueandfail-whena missing SANDBOXED id — would create the unlabelled issue and then fail. That is strictly worse than doing nothing: the untrusted issue exists, unlabelled, and the caller is told the call failed, so nothing retries the labelling.A control that leaves the hazard in place and returns an error is not a control.
What is left, and it is a real choice
required=trueon alabelsarray does gate the request, since a missing required field is a schema rejection before the call. That forces the model to send a label. It cannot pin which, so the model could satisfy it with any label.Or the wrapper applies it.
create issueis already a shadowed verb in that guardfile — the same mechanism that turnsissue commentinto get-then-comment could turncreate issueinto create-then-label, with the label supplied by the wrapper rather than the model. That is the only shape I can see that is actually enforced, and it does not depend on the model cooperating.I am not writing that shadow. It lives in
agentic-os, it changes a boundary both lanes mount, and after establishing that my first design would have been worse than nothing I would rather hand over a correct problem statement than a second guess.The question for whoever owns that guardfile
Can a shadowed verb inject a body field the caller did not supply? If yes, this is a small change in
agentic-osand the control is real. If no, the honest answer is that the guard layer cannot enforce this and the label has to come from somewhere else — and that is worth knowing before anyone writes more of the policy that assumes it can.Answered, and it is the "no" branch — Angie (ENG, claude seat). The guard layer cannot enforce this, and I said that was worth knowing before more policy assumes it can.
Shadows can inject. MCP guardfiles cannot have shadows.
An
actionshadow inaosguard's guardfile composes calls, and thecallblock's args are written by the guardfile author rather than the caller:So on that surface, injecting a label the caller never supplied is straightforward.
But Echo does not file through that surface. It files through
wrap ward mcp sirens-echo-forgejo, and there is noactionblock in any.mcp.kdlin this portfolio — I grepped rather than assumed. The MCP guardfiles carrycangrants only: a permission layer, not a composition layer. Nothing there can add a call or inject a field.So the guard layer is out, and one layer is left
The harness mediates every tool call.
MCPProvidersees the call and its result, and the definition already names itsissue_tracker, so the harness knows which server is the tracker. After a successful create-issue against that server it can make the label call itself, with the label id from deployment config.That is the only place I can find where the label does not depend on the model choosing to cooperate — which is the whole point of Delphi's argument that this is a security control rather than bookkeeping.
It is harness work, and therefore mine.
What it cannot be
Not
required=trueon a labels field: it forces a label, not the label. Notfail-when: the issue is created and then the call reports failure, leaving the hazard in place with nothing retrying. Not a guardfile shadow: that construct does not exist on this surface.The honest caveat, since this is a security control
Harness-applied labelling is create-then-label, so there is a window where the issue exists unlabelled. Automatic and immediate, but not atomic. The Forgejo API's create-issue accepting labels would close it, and the repository-fixed guardfile simply does not grant that field today — which is the smaller change, and it is a deploy-side grant rather than harness work.
So there are two real options and they belong to different people. That is the decision I would want made before either is built, rather than discovered when both exist.
Correcting my own comment above — Angie (ENG, claude seat). I split this into two options for two people and it is one design that needs both halves.
I wrote that harness labelling is create-then-label with a window, and that a deploy grant for
labelson create-issue was a separate, smaller option. That framing is wrong, and the right one is better than either.The harness can inject into the create call itself
internal/community/mcp.go:623,mcpToolSession.Call, is the single choke point every MCP tool call passes through:The harness holds
argumentsbefore dispatch. So it can addlabelsto a create-issue call rather than making a second call afterwards.Which makes it one design
issue_trackerlabelsoncreate issuein the repository-fixed guardfile, since the call is rejected today for carrying a field the grant does not listBoth are needed and neither works alone. And together it is atomic — one API call, issue and label at once — with no window, and the model cannot omit it because the model never supplies it.
That is strictly better than the create-then-label I proposed, and I would have built the weaker one if I had stopped at the previous comment.
Why I am still not building it tonight
This is the third design I have proposed for one control. The first would have been worse than nothing, the second had a window, and this one I believe is right — which is exactly the point at which I want someone else to read it before it becomes a security boundary, rather than my third guess landing because it is my third.
Both halves are small and now fully specified. The label id is
353, repository-scoped to sirens-echo, which is correct because that guardfile is repository-fixed.CLAIM — Angie (ENG, claude seat) · 2026-08-14T10:48Z · 20 min, expect a second. Building both halves.
Correcting my own reason for holding off: I said I wanted someone to read the design before it becomes a security boundary. The merge gate is that reading. This repository is on
pull-request-and-mergeand nothing lands without a director merge, so the choice was never build-versus-review — it was build-and-be-reviewed versus leave-it-sitting. I picked the wrong one and the third design does not become safer by waiting.Both halves, as specified above: the harness injects the label into the create-issue arguments before dispatch, and the deploy guardfile grants the field so the call is not rejected for carrying it.
Safe by default: no configured label id means no injection and today's behaviour exactly.
All three pieces exist — Angie (ENG, claude seat). Delphi's amendment is implemented end to end.
labelsfieldAfter that rolls, an issue Echo files arrives already labelled, in one request. No window where member-influenced text sits in the tracker unmarked, and no dependence on the model choosing to make a second call.
Why I turned it on rather than leaving it configured-off
The content gate and the scratchpad both landed switched off, because enabling each costs something you should weigh. This costs nothing — one field on a call already being made, against a label that already exists. Leaving it off would have meant shipping a security control you decided, in three merged pieces, that does nothing.
If you would rather it stayed dark until you say so, it is one line to remove and I would rather be corrected on a working control than have you find an inert one.
The reasoning that got here, since it took four attempts
Asking the model — prompt-level, wrong layer. A
fail-when— reports after the issue exists, so the hazard stays and nothing retries. A guardfile shadow — that construct is on the CLI surface, not the MCP one. Create-then-label — leaves a window.The harness holds the arguments before dispatch, which is the only place all four failures are avoided at once. I would have shipped the first of those if I had reasoned about
fail-wheninstead of grepping what it actually does.The last piece landed, and the contradiction a previous seat named did not get fixed with it. #586, gate green, carrying
closes #208. Angie (ENG) · seatclaude-macos-…-ee99.All three. The control is live.
What was left, and it is not bookkeeping
That seat wrote, twice, that
AGENTS.mdsays Echo's Forgejo issues carry no labels, and that "whichever way this lands, that sentence changes with it." It landed and the sentence did not.This is worse than an ordinary stale doc, which is why I am spending a change on it rather than noting it:
AGENTS.mdis loaded by every agent working this repository.Corrected to say what the service actually does, with a pointer to the doc that explains it.
On turning it on rather than shipping it dark
Recording agreement rather than re-deciding, since a later reader may wonder: the content gate and the scratchpad both landed off because enabling each costs something. This costs one field on a request already being made. Shipping three merged pieces of a security control that does nothing would have been the worse default.
Closing
Four designs were proposed for this control and three were wrong: a prompt instruction, a
fail-whenthat reports after the issue exists, a guardfile shadow on a surface that has no shadows, and create-then-label with a window. The one that shipped injects before dispatch, which is the only point where all four failures are avoided at once.That reasoning is worth keeping in the thread rather than compressed into a commit, because the next person to add a control on this surface will start at option one.