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 stillOperatorTaskSnapshotReading.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.jsmapped any non-200 propose to 502 — and rig-cockpit-worker#221 fixed it hours later:PROPOSE_DECISIONSnow maps each code to an operator-facing sentence, andintent_already_satisfiedreads "This change is already live on the page." (rig-cockpit-worker#220, closed.) AnchorClassesis 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.
CockpitTaskEndpointsE2eTestshosts getPreStateDashecorpSiteProbeby 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.