Propose the X Article uploader as a new project
Blog posts live as Markdown under the site content tree, but X Articles has
no Markdown import: its draft endpoint accepts only a DraftJS content_state,
and publication is a separate authenticated call. Republishing a post by hand
means recreating its structure, inline styles, links, and code blocks in X’s
editor.
Record the planning artifacts for projects/x_article_uploader, which compiles
a post into a content_state document and can create and publish it. Conversion
stays offline, deterministic, and credential-free; publication is the only
path that uploads media and calls the API, so a draft can be reviewed before
anything becomes a post.
Write the three capability specs, the design, and the task list. The design
records the measured reasons the converter parses Markdown instead of scraping
Hugo-rendered HTML: only seven render hooks exist, with no paragraph, list
item, or emphasis hook, and hooks expose no offsets for the inline ranges that
DraftJS requires. Headings map by clamping, since X exposes three levels, code
fences and tables are preserved as markdown entities because X has no table
construct, footnotes become a trailing section, and images become skeletons
that publication resolves by uploading media.
Address the review by stating the input model explicitly: a source post is
front matter plus body, the parsed title is carried to draft creation because
content_state cannot hold it, and a missing title fails conversion. Correct
the entity schema inventory, which had listed url as an accepted field without
noting it is documented for link entities, and record that the media endpoints
accept no SVG type while all six current image references are SVG, so the
converter reports those images instead of emitting entities that cannot
resolve. Discover the covered posts from the content tree so
diagrams-in-ac-era is no longer omitted, and reorder the tasks so behavior
coverage and failure cases are written before the implementation they check.
Name the approved Markdown parser, github.com/yuin/goldmark, and record why
the already-present rsc.io/markdown is not a substitute: its table support is
marked TODO and it parses no footnotes, while a current post has an 11-row
table and a footnote. Give the end-to-end check a per-post expected outcome,
because a post whose images the upload endpoints reject must fail with the
named diagnostic rather than satisfy the mapping contract. Add the raster-only
image policy for posts from now on, with a WebP raster render path for Mermaid
diagrams and authoring guidance that states it, since no SVG image can be
uploaded.
Carry the artifact the publisher actually needs and the requirements the
second review found missing. The draft artifact records each unresolved
image’s source locator beside the document, because the API entity data object
rejects additional properties and cannot hold a path, and publication resolves
the bytes from that locator without re-parsing Markdown. Inline ranges are
measured in UTF-16 code units, since a byte or rune offset selects the wrong
characters after a supplementary character. Plan the uploader’s al.lua,
al_config target, and injector wiring, and give the raster policy, the raster
render rule, and the uploader’s Vault identity their own owner changes:
projects/alwaldend.com/openspec/changes/add-raster-blog-images,
tools/mermaid/openspec/changes/add-raster-diagram-renders, and
infra/vault/openspec/changes/add-x-article-uploader-credentials. Register the
new tools/mermaid workspace for validation.
Extend the locator contract and the reuse state the third review asked for.
Each locator records the post package its relative path resolves against, so a
moved artifact still identifies its base, and a digest of the bytes as they were
at conversion time, so the publisher has the value it tests the current bytes
against. Publication keeps the digest-to-media_id mapping in its own cache under
the task’s ignored output directory, since a digest alone cannot recover an
identifier from an earlier process and the reviewed artifact must stay
unchanged. Register both new workspaces, this project’s and tools/mermaid’s, in
the repository validation map.
Map the last constructs the fourth review found unstated: an inline-code span
keeps its literal text with the lost code styling reported as a continuing
diagnostic, a thematic break becomes an atomic block with a divider entity
because X exposes divider for exactly that purpose, and conversion diagnostics
now distinguish constructs whose content would be lost, which fail, from
formatting that cannot be reproduced, which does not. Bind each locator to the
entity it resolves by entity key so publication attaches a media_id by identity
rather than by position. Give the Vault owner change its own implementation
tasks and make the uploader carry it out, reorder the raster group so its
end-to-end check precedes the render path, have the publisher’s coverage task
state its success cases before implementation, and drop the duplicated
missing-credential requirement from the Vault spec so each fact keeps one owner.
Give those new mappings the rest of their chain rather than leaving the
requirements unowned: a design decision recording why inline code keeps its text
while a thematic break becomes a divider, converter tasks 4.11 and 4.12 that
implement them and bind each locator’s entity key, and a behavioral-tests
contract that names the continuing diagnostic as part of a conforming post’s
recorded outcome instead of only the failing one.
OpenSpec-Change: projects/x_article_uploader/openspec/changes/add-x-article-uploader
Also-Changed: projects/alwaldend.com/openspec/changes/add-raster-blog-images, tools/mermaid/openspec/changes/add-raster-diagram-renders, infra/vault/openspec/changes/add-x-article-uploader-credentials, infra/src/openspec/validation/BUILD.bazel
LLM-disclaimer: This commit was generated by an LLM.