feat: drop the issue envelope and let the model file through its own tool #109

Merged
coilysiren merged 1 commit from feat/drop-issue-envelope into main 2026-08-11 17:04:28 +00:00
Member

The model was handed forgejo__ tools on every turn while the response envelope carried a parallel path to the same capability. One mechanism now. Net -595 lines.

Replies are plain text

ParseReply bounds the reply and does nothing else: no JSON decode, no embedded-object scan, no fence unwrapping.

That last removal is a correctness fix, not just cleanup. A fenced block is legitimate reply content, and the old parser unwrapped fences before parsing, so an answer containing a shell snippet would have been mangled. There is a test pinning the verbatim round-trip.

Removed

Decision, IssueDraft, the draft normalizer and its Discord-identifier sanitizer, ForgejoMCPClient and the whole forgejo.go write path, forgejo.issue.ensure, appendTrackingLink, the forgejo_issue_failed exception, and the "issue draft while tracking is disabled" guard.

Two deliberate deviations from the suggested scope

The repair round stays. The issue says it exists to absorb malformed JSON. It also catches neutral-style violations, and that half is still worth having. Its prompts drop the JSON instructions and keep the style ones, and its tests now trigger on a style violation and on the length bound. Deleting it wholesale would have quietly removed style enforcement along with the JSON handling.

issue_tracker stays in the definition. It no longer selects an envelope field, it selects whether the prompt tells the model to file at all, and config still validates that it names a configured MCP server. Echo gets the filing instruction; Deep has no tracker and correctly gets none.

Preserved behavior

The prompt now instructs the model to search for an open issue with the same title before filing, which keeps the exact-title reuse that the deleted EnsureIssue did in code. Labels stay forbidden, the redaction rules stay, and ValidateGrounding still rejects any claim of a completed action that no tool result in the turn confirms — which now covers the real filing path rather than a runtime side effect.

The two evaluation cases keyed on required_issue_kind now key on required_tool: forgejo__create_issue, the same expectation through the new mechanism.

Verification

go test ./..., sirens-echo-policy-check, and pre-commit run --all-files green. Snapshots regenerated. Docs updated: FEATURES, config, observability, and tools all described the envelope.

Sequencing

This assumes the guarded create_issue grant is reachable from the deployment. Per my earlier comment the tracked guardfile already has can list issue and the /api/{tool} projection is a supported ward-mcp surface, so the deployment work is the separate task. Worth landing that first, or at least confirming it, since with the envelope gone a broken tool grant means knowledge gaps are silently not filed rather than filed-and-failed.

Refs #102

The model was handed `forgejo__` tools on every turn while the response envelope carried a parallel path to the same capability. One mechanism now. Net **-595 lines**. ## Replies are plain text `ParseReply` bounds the reply and does nothing else: no JSON decode, no embedded-object scan, no fence unwrapping. That last removal is a correctness fix, not just cleanup. A fenced block is legitimate reply content, and the old parser unwrapped fences before parsing, so an answer containing a shell snippet would have been mangled. There is a test pinning the verbatim round-trip. ## Removed `Decision`, `IssueDraft`, the draft normalizer and its Discord-identifier sanitizer, `ForgejoMCPClient` and the whole `forgejo.go` write path, `forgejo.issue.ensure`, `appendTrackingLink`, the `forgejo_issue_failed` exception, and the "issue draft while tracking is disabled" guard. ## Two deliberate deviations from the suggested scope **The repair round stays.** The issue says it exists to absorb malformed JSON. It also catches neutral-style violations, and that half is still worth having. Its prompts drop the JSON instructions and keep the style ones, and its tests now trigger on a style violation and on the length bound. Deleting it wholesale would have quietly removed style enforcement along with the JSON handling. **`issue_tracker` stays in the definition.** It no longer selects an envelope field, it selects whether the prompt tells the model to file at all, and config still validates that it names a configured MCP server. Echo gets the filing instruction; Deep has no tracker and correctly gets none. ## Preserved behavior The prompt now instructs the model to search for an open issue with the same title before filing, which keeps the exact-title reuse that the deleted `EnsureIssue` did in code. Labels stay forbidden, the redaction rules stay, and `ValidateGrounding` still rejects any claim of a completed action that no tool result in the turn confirms — which now covers the real filing path rather than a runtime side effect. The two evaluation cases keyed on `required_issue_kind` now key on `required_tool: forgejo__create_issue`, the same expectation through the new mechanism. ## Verification `go test ./...`, `sirens-echo-policy-check`, and `pre-commit run --all-files` green. Snapshots regenerated. Docs updated: FEATURES, config, observability, and tools all described the envelope. ## Sequencing This assumes the guarded `create_issue` grant is reachable from the deployment. Per my earlier comment the tracked guardfile already has `can list issue` and the `/api/{tool}` projection is a supported ward-mcp surface, so the deployment work is the separate task. Worth landing that first, or at least confirming it, since with the envelope gone a broken tool grant means knowledge gaps are silently not filed rather than filed-and-failed. Refs https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/102
feat: drop the issue envelope and let the model file through its own tool
All checks were successful
ci / test (pull_request) Successful in 35s
ci / publish-echo-image (pull_request) Has been skipped
3b6abd530a
The model was handed `forgejo__` tools on every turn while the response
envelope carried a parallel path to the same capability. One mechanism now.

Replies are plain text. `ParseReply` bounds them and nothing else: no JSON
decode, no embedded-object scan, no fence unwrapping. That last one was a
correctness bug waiting to happen, since a fenced block is legitimate reply
content and the old parser would have unwrapped it. There is a test for it.

Removed with the envelope: `Decision`, `IssueDraft`, the draft normalizer and
its sanitizer, `ForgejoMCPClient` and the whole `forgejo.go` write path,
`forgejo.issue.ensure`, `appendTrackingLink`, the `forgejo_issue_failed`
exception, and the "issue draft while tracking is disabled" guard.

Two deviations from the issue's suggested scope, both deliberate.

The repair round stays. The issue says it exists to absorb malformed JSON, but
it also catches neutral-style violations, and that half is still worth having.
Its prompts drop the JSON instructions and keep the style ones, and its tests
now trigger on a style violation and on the length bound rather than on broken
JSON. Deleting it would have quietly removed style enforcement.

`issue_tracker` stays in the definition. It no longer selects an envelope
field, it selects whether the prompt tells the model to file at all, and
config still validates that it names a configured MCP server. Echo gets the
filing instruction, Deep has no tracker and correctly gets none.

The prompt now tells the model to search for an open issue with the same title
before filing, which preserves the exact-title reuse the deleted `EnsureIssue`
did in code. Labels stay forbidden, redaction rules stay, and the runtime still
refuses any claim of a completed action that no tool result in the turn
confirms.

The two evaluation cases keyed on `required_issue_kind` now key on
`required_tool: forgejo__create_issue`, which is the same expectation expressed
through the new mechanism.

`go test ./...`, `sirens-echo-policy-check`, and `pre-commit run --all-files`
are green. Snapshots regenerated.

Refs #102

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
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-gaming/sirens-echo!109
No description provided.