Build the dev.to syndication upsert #103
Labels
No labels
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
burndown-2026-06
burndown-2026-08
icebox
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilysiren/website#103
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
The goal is programmatic sync of promoted posts to dev.to that can be re-run at will, so an edit on coilysiren.me propagates rather than creating a duplicate.
dev.to runs Forem, whose v1 API supports both create and update, which is the property that eliminates most alternative platforms. Medium stopped issuing integration tokens in 2025 and cannot be scripted. Substack has no public write API.
POST /api/articlescreates,PUT /api/articles/{id}updates,GET /api/articles/melists.api-keyheader.canonical_url, which keeps coilysiren.me the SEO original.Required outcome
A ward-invocable script that pushes the promoted set to dev.to idempotently. Running it twice in a row produces no second copy and no spurious edit.
Acceptance criteria
.ward/ward.yamlverb declared before first use.canonical_urlto thehttps://coilysiren.me/posts/<slug>/original.canonical_urlmatched againstGET /api/articles/me, not on title. Retitling a post must update rather than duplicate.https://coilysiren.meURLs. The Terraform post's HTML author-note comment must not break the dev.to renderer.Sequencing
Blocked on the promotion-flag issue, which supplies both the allowlist and the indexable canonical target. Publishing while the canonical target is
noindexwould hand dev.to the indexed copy.Human checkpoint
First live publish is externally visible and creates public artifacts under Kai's name. Kai approves the dry-run plan before the first write.
Non-goals
Frontend seat. This is unblocked now, and it is not mine to build. Handing it over with the corrections it needs, and with my half already delivered.
Unblocked
The dependency is cleared.
#97landed the promotion flag, so the allowlist exists, and#99landed the feed. The three promoted posts are indexable, so the sequencing risk this issue names, "publishing while the canonical target isnoindexwould hand dev.to the indexed copy", no longer applies.Why I am handing it over rather than building it
The script is an authenticated API client with SSM secret handling and idempotency state. That is foundational software, which the frontend seat defers to the platform seat. Publishing Kai's writing to dev.to is communication addressed outward, which is the Developer Advocate's. Neither is mine, and Kai confirmed the split.
What was mine is done: the
promotedflag this reads as its allowlist, and the canonical target it must match.Three corrections before anyone builds it
1. The canonical host is wrong, and it matters most here. The acceptance says
https://coilysiren.me/posts/<slug>/, twice. The site declares:The apex 301s to
www. If dev.to'scanonical_urlnames the apex while the site nameswww, the two disagree and every syndicated article points through a redirect. That is exactly the defect#123item 2 existed to fix, and this issue's whole SEO argument rests on the canonical being right.Use
https://www.coilysiren.me/posts/<slug>/. Better still, read it from the post's own computedcanonicalrather than rebuilding the string, so it cannot drift again.2.
.ward/ward.yamlverbs are retired. The acceptance says to declare a new verb there before first use. That file carries catalog metadata only now, and dev verbs live in thejustfile. The verb goes there.3. One acceptance criterion has nothing to act on. "Relative links and image paths in post bodies are rewritten to absolute URLs" is worth keeping as a guard, but all three promoted posts currently contain zero relative links or images. Do not build an elaborate rewriter for a case that does not exist yet.
The other half of that criterion is real. The Terraform post does carry an HTML comment at line 55:
That one needs handling against the dev.to renderer.
What stays correct, and is worth keeping
Two design choices here are better than the obvious version and should survive the rewrite:
canonical_url, matched againstGET /api/articles/me, not on title. Title is the obvious key and the wrong one, because retitling a post would duplicate rather than update.The platform survey is also worth not re-running: Forem's v1 API supports create and update, Medium stopped issuing integration tokens in 2025, and Substack has no public write API. That is why this is dev.to and not somewhere else.
Worth a moment before building
Kai closed
#98today on the grounds that a three-post archive, two of them years old, was not worth advertising. The same arithmetic applies to what this syndicates:She chose to keep this one moving anyway, and nothing else is blocked on it either way. Flagging it so the implementer knows the reach is thin today and the value grows with new writing rather than with this batch.