Skip to content

The marker names its template, and the diff rule follows it

What was pinned

OperatorTaskDiffPolicy.Decide took only the file list and compared it to three constants — the path from OperatorTaskPolicy, and OperatorTaskIssueTemplate's two anchors. A PR that is exactly right for any other template therefore read Differs, which the release hold turns into a human click.

That is a manual step on every non-forward run, in a loop whose stated requirement is that there are none. It is also the blocker named in rig-conductor#2264's own body.

Why the marker is the carrier

MergeGate has no request and no store access. Its only input about which change was consented to is the issue marker — and the marker already named the template, then threw it away, because ExactMarker hard-coded the id and the digest as regex literals rather than capturing them.

So the parse now captures both and resolves the id through the registry (OperatorTaskTemplates.ById, rc#2257), requiring the marker's digest to equal that template's own derived digest.

The marker is a selector, never an authority. It can only select something already registered, carrying that registration's own digest. A hand-edited marker can make a PR unjudgeable — which holds it for the operator — but never judgeable against a different intent.

Fail closed, in two places

Classification keeps the loose test IsTaskIssueBody (any marker-shaped comment) still decides whether this is a task PR, so a damaged marker cannot buy a PR its way past the gate. The exact parse only supplies the template.
No template is not a default template A task PR whose exact marker does not parse yields a null template, and MergeGate answers Unreadable → Hold. Decide throws on a null template rather than reaching for one.

Proved by mutation

A green run proves nothing here — the suite was green before, against one template.

Mutation Result
Decide compares to OperatorTaskIssueTemplate's anchors again 2 fail, including "a diff is judged against its own template"
The marker resolves to v1 regardless of the id it names 3 fail
The digest is not compared to the registry 2 fail
Restored 2474 pass / 0 fail

The two most valuable tests are the ones that could not have been written before: the same diff is Matches under its own template and Differs under another, and a reverse template accepts the reverse diff — which is precisely what makes an iterable target possible.

Still pinned after this

This slice moves the rule, not the whole pipeline:

  • The issue Title and Body are v1 constants, and CockpitTaskConfirm ignores the stored TemplateId. Nothing else may be registered until that is fixed, or a second template would file the Reward-E issue verbatim.
  • SiteUrl and the deploy.yml run URL are constants (OperatorTaskSnapshotReading:54-55).
  • RefuseTemplate still compares the signed body's id to one id.
  • GitHubTaskSnapshotReader takes its template by injection, mirroring what rc#2253 did for DashecorpSiteProbe. The status route holds the request, and therefore its registered template, so a later slice should pass it at call time and delete the default.