Skip to content

The issue comes from the task's own template

rig-conductor#2270. Follows the marker names its template (#2266).

What was pinned

CockpitTaskConfirm filed OperatorTaskIssueTemplate.Title and Body(requestId, attemptId), both v1 constants, and ignored the stored TemplateId. After #2266 the rule a PR is judged by follows the template, but the request Dev-E is given did not. A second registered template would have filed the Reward-E issue word for word, and Dev-E would have made the Reward-E change under the new template's marker. MergeGate would then judge that diff against the new template, read Differs, and hold it for a human.

That is why #2266 said nothing else may be registered until this was fixed.

What the issue is made of now

Part Source Can it drift from the diff rule?
Title the template's IssueCopy.Title Only if its words lie. The registry test covers every entry.
Before and after blocks derived from OldAnchor and NewAnchor, indented by IssueCopy.Indent No: they come from the same values OperatorTaskDiffPolicy compares
Change intro, tail, acceptance, out of scope, after merge the template's IssueCopy Checked: every registered body must contain its own path, href and both anchors, and no other entry's new anchor
Marker the template's own id and derived digest No

OperatorTaskIssueCopy is registry data that lives beside the anchors. It is not input: no marker or issue body can supply it, because a marker only selects a registered template (#2266).

The copy is deliberately not part of CanonicalIntent. The digest binds the change (repo, path, from, to). Hashing the prose too would turn every wording fix into a new intent, and the Cockpit Worker's frozen TASK_INTENT_DIGEST would stop matching it.

Recovery follows the same template

An ambiguous create is recovered by listing the target repo's recent issues and binding the one that carries this attempt's marker. After this change the create posted to template.Repo, but at first recovery still listed v1's repo, stamped its evidence with v1's repo, and OperatorTaskPolicy.Reconcile rejected any other repo. Review-E caught it. All three now resolve the task's stored template. A task whose template is no longer registered is not reconciled at all: no other template's repo could hold its issue.

With one registered repo, the evidence stamp and the policy check behave the same either way. Only the list path can be told apart today, through an unregistered template. The proof for the other two arrives with the first template in a second repo, which also needs the issue creator's single-repo allowlist (repo_not_allowed) lifted.

Fail closed

  • A template with no copy cannot file an issue. IssueCopy throws instead of borrowing another template's words. A template that exists only to judge a diff, such as a test fixture, stays wordless.
  • A claim naming an unregistered template files nothing. OperatorTaskPolicy.Valid resolves the id at proposal time, so this happens only if the registry loses an entry that a live task still names. The lookup throws inside the one-POST block. That throw is treated as Ambiguous, so the attempt is recorded as OutcomeUnknown and never retried.

The parser-hazard test (no checkbox, dependency phrase, epic, closing keyword or #N) now runs over every registered body, not only v1's. Each body goes into Dev-E's prompt verbatim, so a new entry's wording can stall dispatch just as v1's could.

v1's prose moved into its registry entry verbatim. A byte-for-byte golden test (body length and sha256) proves it was moved, not rewritten.

Proved by mutation

Mutation Result
Confirm uses v1 regardless of the stored template 1 fails: a claim naming an unregistered template files no issue
Body uses v1's copy regardless of the template 2 fail, including a second template files its own issue, not the first one's
A second entry registered whose out-of-scope text says Depends on #12. 1 fails: body has zero hazard hits for the rig parsers
Reconciliation lists v1's repo regardless of the stored template 1 fails: a task naming an unregistered template is not reconciled against another template's repo
Restored Core 2480 pass / 0 fail

Still pinned after this

  • RefuseTemplate still compares the signed body's id to one id (CockpitTaskRoutes.cs), and IOperatorTaskStore.ProposeAsync takes no template.
  • The status route's probe, and GitHubTaskSnapshotReader, take their template by injection with a v1 default. Until the template is passed at call time, a second template can never reach Verified.
  • The Cockpit Worker freezes one TASK_TEMPLATE and TASK_INTENT_DIGEST.
  • SiteUrl and the deploy.yml run URL are constants (OperatorTaskSnapshotReading.cs).