fix-ship-note-linksrun.mdlane/output/notes.md#product-update with a literal Dig deeper: <merged-PR URL>, and
release.md files sit on main permanently reading merged: pending · expected: the note names
the PR it is about, and the run record states something that is true.investor-update.md is emailed by ship-note.yaml on the merge event, and by
then the branch is merged and gone, so nothing in pipeline/runs/<slug>/** can be corrected
without pushing to main.pipeline/stages/06_ship/CONTEXT.md — step 3 now fills the Dig deeper links before the
merge (both are already known: run.md's pr: line and the changelog page's live URL) and says
why they cannot be filled later; step 5 is narrowed to the one post-merge edit that genuinely
works, the PR-body spec-link repoint via the API; the release.md template's
merged: <yes — when / no> becomes merge: <authorised … | not authorised …>; and a Verify
bullet forbids a run folder predicting its own merge.
.github/workflows/ship-note.yaml — a Refuse to send an unfilled note step fails the send when
a note's Dig deeper line still carries an angle-bracket placeholder, routing to the existing
#alerts failure step instead of mailing the channel something broken.The evidence is the shipped record itself, across all 11 runs that have written a ship note:
Dig deeper placeholder — 3 of 11 shipped unfilled:
REFUSE demo-scope-visuals Dig deeper: <merged-PR URL> · <changelog entry URL>
REFUSE preview-db-seed Dig deeper: <merged-PR URL>
REFUSE preview-db-wiring Dig deeper: <merged-PR URL>
The other eight carry real URLs — so the fill is achievable, but only by ignoring the step that says to do it after the merge. Whether a run gets it right is currently down to whether the agent followed the instruction or worked around it.
release.md merge line — every value is wrong in a different way:
Value on main |
Runs | Why it is wrong |
|---|---|---|
merged: yes — … |
6 | written before the merge — a prediction, not a record |
merged: pending |
3 | never updated, so permanently stale once the merge lands |
merged: no — held |
1 | demo-scope-visuals, whose PR #798 did merge |
demo-scope-visuals is the sharpest case: its record says the PR was held at the gate, and #798 is
on main. Six runs guessed right, three left a placeholder, one is flatly contradicted by history —
which is what a contract asking for facts that do not exist yet produces.
ship-note.yaml parses, and its step order is
Checkout → Work out which runs shipped → Refuse to send an unfilled note → Send → Tell #alerts.
The guard was run against all 11 existing notes: it refuses exactly the three known-bad ones and
passes the eight with real URLs — no false positives on ·-separated URLs, em-dashes or prose.
release.md files on main are left as they are.
They are the historical record of what actually shipped; rewriting them to say something the
emails did not say would trade one inaccuracy for another. The fix stops the next one.ship-note.yaml's last step renders in the Actions UI as "Tell" — its name is
Tell #alerts the send broke, unquoted, so YAML truncates it at the #. Pre-existing, cosmetic,
and a one-character fix, but outside this bug's cause; flagged rather than swept in.