Files
claude-plugin-inference-arb…/references/issue-template.md
T
2026-07-29 17:50:20 +03:00

6.5 KiB

issue-template.md — the issue body contract

The shape of every issue this plugin causes to be filed on a target plugin's own repo (output path A, design/plan.md §10, FR-6). Loaded at runtime by bin/filing-plan, which renders it; it is kept here rather than in the script body so that a change to what an issue says is a reviewable diff.

Format is the same as signals-catalog.md: each ## <section> heading below is followed by exactly one fenced block, and the fenced block is the only part the parser reads. Prose outside the fences is for humans.

Placeholders are {{name}}. Every placeholder in a template must be supplied — filing-plan fails loudly on an unknown or missing one rather than rendering a half-filled issue.

Who decides what

filing-plan renders the body, because the body is the contract: the marker, the measurement, the rubric verdict, and the falsifiability triple are all already-computed values and rendering them is a pure-script step.

It does not decide where the issue goes. Repo, final title, labels beyond token-offload, and milestone are anxious:issuer-agent's call, per its own taxonomy. The issue-title section below is a proposal passed to issuer, not an instruction.

The marker is load-bearing

The first line of every body — and of every update comment — is

<!-- ia-candidate: <candidate_id> -->

It is how a re-run recognizes its own prior work (FR-6.3). It renders invisible in Gitea, is stable across runs because candidate_id is, and must never be edited by hand on a filed issue. Removing it from an issue causes the next audit to file a duplicate — which the spec names as the single most likely way for this plugin to become hated.

issue-title

A proposal. issuer may rewrite it.

token-offload: {{headline}} ({{skill}})

issue-body

{{examples}} renders as a markdown list, one input→output pair per line, with the edge case marked. {{determinism_tests}} renders as T1 ✓ T2 ✓ …, using ✗ for a failed test. {{evidence}} always renders — it states whether the cost claim is measured, thin, or unmeasured, so a reader never has to guess whether the number is evidence or arithmetic.

<!-- ia-candidate: {{candidate_id}} -->

**Measured cost.** {{measured_cost}}

**Evidence strength.** {{evidence}}

**Boundary position.** {{position}} — confidence {{boundary_confidence}}.
Determinism tests: {{determinism_tests}}

**Proposed signature.**
`{{signature}}`

**Examples.**

{{examples}}

**When a human would overrule this.** {{overrule_case}}

**Escalation path.** {{escalation_path}}

**What crosses the boundary.** {{digest_schema}}

---

This is a proposal from an audit, not an accepted design. It is deliberately
falsifiable: if the overrule case above is *common*, the cut belongs earlier in
the pipeline than proposed, and this issue should be reshaped rather than
implemented. Closing this as `wontfix` is a useful outcome and is tracked as a
calibration signal.

Audit: `{{audit_page}}` on the {{wiki_repo}} wiki. Rubric v{{rubric_version}}.

update-comment

Posted instead of a new issue when the marker is already present on the repo (FR-6.3). It carries the marker again so that the search finds this issue whether the marker lives in the body or only in a comment.

<!-- ia-candidate: {{candidate_id}} -->

**Re-measured {{run_date}}** — window {{window}}. Status: {{status}}.

{{measured_cost}}

Evidence strength: {{evidence}}

Boundary position {{position}}, confidence {{boundary_confidence}} (rubric
v{{rubric_version}}).

Audit: `{{audit_page}}` on the {{wiki_repo}} wiki.

How the pre-filing search is actually performed (FR-6.3). This is not hand-waved: both queries below run against the target repo, through anxious:issuer-agentcluster:gitea-agent, and their merged results are what filing-plan plan --existing consumes.

Three steps, not one. The obvious one-query design does not work, and the reason was established empirically against the live Gitea (2026-07-29, probe issue kotkan/claude-plugin-inference-arbitrage#9, since closed):

Neither search_issues nor list_issues returns the issue body. Both return metadata only — number, title, state, labels, timestamps. A marker match therefore cannot be made from a search result. The body must be fetched per issue with issue_read(method="get"), and comments with issue_read(method="get_comments").

Also established by the same probe: search_issues does full-text match on body content, including text inside an HTML comment (a query for a string that appeared only in the body, never in the title, returned the issue). So the full-text path works — it just cannot be trusted to confirm a match, only to suggest one.

1. candidates   list_issues(owner, repo, labels=["token-offload"], state="all")
                search_issues(query="ia-candidate: <candidate_id>", owner, state="all")
                -> union of issue numbers; bodies are NOT in these responses
2. fetch        issue_read(method="get", index=N)           -> body
                issue_read(method="get_comments", index=N)  -> comment bodies
                (comments only needed when the body has no marker)
3. match        filing-plan plan --existing <merged.json>   -> local literal match

Step 1's label scan is the authoritative half: every issue this plugin files carries token-offload, so it is a bounded, exact superset of the issues that can carry a marker. The full-text query is the safety net for an issue whose label a human removed. Step 3's match is a literal string comparison and is the only thing that decides identity — the search engine never does.

If step 1 finds no token-offload label on the target repo, that is the first-audit case: there are no prior issues, issuer creates the label with the first filing, and the empty result is correct.

The merged shape handed to --existing is a JSON array of {number, state, title, body, comments: [...]}. filing-plan matches <!--\s*ia-candidate:\s*<id>\s*--> against the body and each comment, so an issue keeps its identity even if a human rewrites the body entirely, as long as one update comment survives — also verified against the live probe issue.

--existing is mandatory, even when empty. filing-plan plan refuses to run without it rather than defaulting to "nothing exists", because that default turns a failed search into a duplicate-filing spree — the exact outcome FR-6.3 exists to prevent.