Skip to content

The success signal was a constant that was already true

rc#2253. Related: #2245 (the merge this was meant to verify), #2238 (the template registry, which must not ship without this).

What was wrong

verified / "Live on dashecorp.com" was decided by one condition:

// OperatorTaskStageProjector.cs, before this change
if (Live(s.Verify)) return (Verified, "Live on dashecorp.com", null);
if (s.Deploy is { Status: "completed" } run && run.Conclusion != "success")
    return (Failed, "Deploy run failed", run.UpdatedAt);
...
private static bool Live(OperatorTaskVerifySnapshot? v) => v is not null && DashecorpAnchor.IsReleasedHref(v.Href);
public const string ReleasedHref = "https://reward-e.com";   // DashecorpAnchor.cs:11

dashecorp.com has served that href since 2026-09-21T17:37Z, when the feature's one real end-to-end run merged dashe-website PR #204. From that moment the first probe of any merged task read the live page, found the anchor, and terminated the task verified — whatever the pull request contained. The check also sat ABOVE the deploy-failure branch by design ("the probe is primary"), so it masked a failed Cloudflare deploy as well.

The loop was therefore unfalsifiable: its terminal state could not distinguish "the change shipped" from "nothing shipped and the page already looked like this". #2245 had just made the merge machine-judged; together that means a task could merge with no human and report success with no evidence.

Pinning a second template would not have fixed it. A registry of from → to pairs leaves ReleasedHref a const the probe never parameterises, so template 2 would be reported live the moment its PR merged, whether or not the page changed.

What it is now

The expected post-state belongs to the template, and it is derived, not written down twice. OperatorTaskTemplate carries OldAnchor, NewAnchor and AnchorClasses, and derives PreviousHref/ExpectedHref from those anchors using DashecorpAnchor.ExtractHref — the same parser the probe runs over the served page. So the thing verified cannot drift from the line Dev-E is asked to write, and a test pins that ExpectedHref is the digested intent's to (what the operator consented to) and PreviousHref its from.

Both are derived on access. A record's with copies stored fields without re-running their initialisers, so a stored expectation would survive a with that changed the anchor it came from — and the mutation tests below all build their mutant with with, so they would have passed while varying nothing. That is the same shape of false proof as the constant they replace.

The probe judges; nothing downstream holds an href. DashecorpSiteProbe is the one place that has the template. OperatorTaskVerifySnapshot now carries Satisfied beside the Href it read, and the projector's Live() is v is { Satisfied: true }. CockpitTaskStatusRead.NeedsProbe and the verify.ok view read the same flag. No new status key: the 15-key contract is untouched and verify.ok already meant exactly this.

A failed deploy run outranks a live anchor, and a failed task dates no deployed or verified timeline entry. "The probe is primary" was defensible only while the anchor could tell before from after.

Propose reads the live page before it stores anything. One uncached GET of https://dashecorp.com/?rig_propose=<requestId>, then CockpitTaskRoutes.RefusePreState:

The page's card HTTP Body
the template's from href — the propose goes ahead
the template's to href 422 {"error":"intent_already_satisfied"}
anything else, or no single card 422 {"error":"pre_state_unexpected"}
unreadable 503 {"error":"pre_state_unreadable"}

This is the half that gives the terminal state its meaning: a task may only be proposed from the from state, so reaching the to state means a change actually shipped. "Nothing to do" is a refusal, and it is not verified. pre_state_unexpected catches the other end — the page is at neither state, so the line the template replaces is not there to replace and Dev-E has nothing to change either. Unreadable fails closed: a task proposed against a page nobody could read cannot be verified against that page later. A propose replay whose page changed in between is refused rather than answered from the store; the refusal is then the true answer and the stored proposal expires on its own.

Which branch the shipped template is in, and why it matters (corrected — #2261). reward-e-website-link-v1's forward change went live on 2026-09-21, so dashecorp.com serves the template's to href and a propose today returns intent_already_satisfied. RefusePreState tests IsExpected first, so it cannot return pre_state_unexpected while the page is exactly at the post-state. The distinction is the reason there are two codes: intent_already_satisfied is recoverable and pre_state_unexpected is not. The gate reads the served page, never the repository, so restoring one line of website/index.html re-arms the template with no conductor change — which is why the template registry (#2238) is not a prerequisite for running the loop again. An earlier revision of this doc named the wrong branch here and that conclusion was drawn from it.

What the mutations prove

Every rule above was broken deliberately and the suite was required to go red. All seven were caught:

# Mutation Caught by
1 The probe is primary again (the pre-#2253 order) A_failed_deploy_run_outranks_a_live_anchor_and_dates_no_stage_the_task_did_not_reach, A_failed_deploy_is_failed_even_though_the_page_serves_the_expected_href
2 A failed task may date deployed/verified from the probe the same two
3 The projector decides live from a constant again A_template_expecting_an_href_the_page_never_serves_cannot_reach_verified
4 The probe ignores its template and calls every read satisfied DashecorpSiteProbeTests (2 tests)
5 Propose stores without reading the live page A_task_the_live_page_already_satisfies_is_refused_at_propose_and_files_no_issue
6 A page at neither state is allowed through A_task_the_live_page_leaves_nothing_to_do_is_refused_before_anything_is_stored (3 cases)
7 The expectation is stored instead of derived (the with trap) A_derived_template_derives_its_expectation_again, and the walk mutation above

The load-bearing one is #3 and the test it breaks. A_template_expecting_an_href_the_page_never_serves_cannot_reach_verified walks a whole task to a merge, serves https://reward-e.com/ from the fake site exactly as the passing walk does, succeeds the deploy run — and requires the task NOT to report verified, then to report "Not live 15 min after the merge" once a probe has read the page past the deadline. That test could not be written before this change: the expectation was a constant, so there was no input to vary.

What this does not do

  • The probe still reads one page. The template carries which anchor and what it must say; the URL (https://dashecorp.com/) is still OperatorTaskSnapshotReading.SiteUrl. #2238's registry must carry the site too if a template ever targets another one.
  • ~~The Worker renders the new refusals as a gateway error.~~ It did when this shipped — tasks.js mapped any non-200 propose to 502 — and rig-cockpit-worker#221 fixed it hours later: PROPOSE_DECISIONS now maps each code to an operator-facing sentence, and intent_already_satisfied reads "This change is already live on the page." (rig-cockpit-worker#220, closed.)
  • AnchorClasses is not in the digest preimage. The intent JSON derives both hrefs through the class tokens but does not name them, so two templates identical but for their classes would share a digest. Only reachable with a second template, so it belongs to #2238; noted here because the omission is in this design.
  • Tests no longer reach dashecorp.com by accident. CockpitTaskEndpointsE2eTests hosts get PreStateDashecorpSiteProbe by default, because a propose through a production-wired host really does make an outbound GET — which is how this was found in the first place: those tests started answering 422 from the live site.