# 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 `##
` 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 ```text ``` 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. ```text 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. ```markdown **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. ```markdown **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. ``` ## marker-search 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-agent` → `cluster: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. ```text 1. candidates list_issues(owner, repo, labels=["token-offload"], state="all") search_issues(query="ia-candidate: ", 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 -> 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 `` 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.