Writing Process: From Draft to Publication

tutorial · 90% AI

Prerequisites


Step 1: Create a post stub

Run create-post with the post title and section:

create-post "My Post Title" --section acceleration

Valid sections: acceleration, tech, seasteading.

This creates one file:

~/av/doc/posts/YYYY-MM-DD-my-post-title/
    YYYY-MM-DD-my-post-title.md

The .md file contains a frontmatter stub:

---
type: long-form
purpose: explainer
title: "My Post Title"
subtitle: ""
description: "My Post Title -- archerships"
date: 'YYYY-MM-DD'
section: acceleration
tags: []
ai_percentage: 0
published_at: []
---

Before writing, set the correct type and purpose. Valid types: short-form, long-form, recipe, table, lightbox, handoff. Valid purposes: coolshare, explainer, propaganda, tutorial, research-brief, tech-brief. See ~/av/pol/WRITING.md for full reference.

Also set ai_percentage (0-100, round numbers): the approximate fraction of text that was AI-generated or AI-edited.


Step 2: Write the post body

Open the .md file and write after the closing --- of the frontmatter.

~/av/doc/posts/YYYY-MM-DD-my-post-title/YYYY-MM-DD-my-post-title.md

Rules:

  1. Do not include an H1 (#) in the body – the renderer generates it from title:.
  2. Use ## for top-level sections, ### for subsections. Never skip heading levels.
  3. Leave one blank line before and after every heading, list, code block, and table.
  4. Verify every link before writing it to the file – no tracking parameters.
  5. For propaganda posts: include a ## Call to Action section at the end.
  6. For research-brief posts: include a ## Sources section; note confidence levels.
  7. For tech-brief posts: prefer tables over prose; end with a ## Reference section.
  8. For tutorial posts: include a ## Prerequisites section first; number steps within each ## section; end with a verification step.

Step 3: Add images

All source images are stored in ~/av/ast/img/ under a short descriptive directory name. Each image directory holds the original plus platform-optimized variants named descriptive-name-platform-WIDTHxHEIGHT.ext.

3.1 Store the original

mkdir -p ~/av/ast/img/deer
cp ~/Downloads/deer-photo.jpg ~/av/ast/img/deer/deer-original.jpg

3.2 Create platform variants

Generate a variant for each platform you intend to publish to. Dimensions are defined in ~/av/pol/TWITTER.md, ~/av/pol/FACEBOOK.md, ~/av/pol/SUBSTACK.md, ~/av/pol/NOSTR.md, and ~/av/pol/ARCHERSHIPS-IMAGE.md.

Common target sizes:

Platform Dimensions Format
archerships 800 x 450 webp
twitter 1200 x 675 png
facebook 1200 x 630 png
substack 1200 x 675 png
nostr 1200 x 675 png

Use the letterbox/pillarbox rule from ~/av/pol/SOCIAL-SETTINGS.md – scale to fit, center on black background, never crop or stretch.

For archerships.com, convert to WebP and compress under 100 KB:

cwebp -q 82 deer-original.jpg -o ~/av/ast/img/deer/deer-archerships-800x450.webp

Adjust quality downward if the file exceeds 100 KB. Do not go below -q 60.

3.3 Symlink into the post directory

ln -s ~/av/ast/img/deer/deer-archerships-800x450.webp \
      ~/av/doc/posts/YYYY-MM-DD-my-post-title/deer-archerships-800x450.webp

3.4 Reference in Markdown

Place the image reference in the body. Banner images go first, before any text:

![A deer standing in a meadow](deer-archerships-800x450.webp){alt="A deer standing in a meadow at dusk, facing left" width="800" height="450" loading="lazy"}

Rules:


Step 4: Render for archerships.com

The renderer to use depends on the post type:

type renderer
long-form, short-form, recipe, handoff render-post.py
table render-table.py
lightbox render-lightbox.py
# long-form, short-form, recipe, handoff:
python3 ~/av/bin/archerships/render-post.py \
    ~/av/doc/posts/YYYY-MM-DD-my-post-title/YYYY-MM-DD-my-post-title.md

# table:
python3 ~/av/bin/archerships/render-table.py \
    ~/av/doc/posts/YYYY-MM-DD-my-post-title/YYYY-MM-DD-my-post-title.md

This writes YYYY-MM-DD-my-post-title.html alongside the .md source.

The preview-essay command auto-detects the post type, renders, and opens the result in Brave with browser-sync live reload:

preview-essay ~/av/doc/posts/YYYY-MM-DD-my-post-title/YYYY-MM-DD-my-post-title.md

Step 5: Build

Run the full site build to copy the rendered post into dst/ and regenerate feeds, home page, and indexes:

bash ~/av/bin/archerships/build.sh

Only posts with a rendered .html file are included – drafts (no .html) are naturally excluded. The build output lands in ~/av/prj/archerships.com/dst/.

Essays and table posts go to dst/essays/. Imported LJ/FB archive posts go to dst/posts/.


Step 6: Deploy to archerships.com

Architecture overview

The site has two serving layers:

6.1 Deploy a new post (fast path)

Use --new-files to deploy only the new post’s files. This skips hashing the full archive (35K+ files) and makes the post live in seconds.

cd ~/av/prj/archerships.com
node ~/av/bin/archerships/deploy-website.mjs \
    --new-files "essays/YYYY-MM-DD-slug.html,essays/image.webp,index.html,feeds/all.xml,feeds/acceleration.xml" \
    --yes

Adjust feeds/acceleration.xml to match the post’s section: - acceleration posts: feeds/acceleration.xml - tech posts: feeds/tech.xml - seasteading posts: feeds/seasteading.xml

The deploy script will: 1. Rsync the specified files to the gateway (site live immediately). 2. Upload them to Arweave (background, free tier for files under 100 KB). 3. Upload an updated manifest and update the ARNS record. 4. Spawn inject-arweave-links.py in the background to add permanent Arweave links to the post footer once the txIds resolve on arweave.net.

6.2 Deploy everything (full deploy)

Only needed when many files change at once (new section, redesign, etc.):

cd ~/av/prj/archerships.com
node ~/av/bin/archerships/deploy-website.mjs --yes

This walks the full dst/ tree, which takes several minutes to hash.

6.3 If Arweave propagation times out

The deploy script waits up to 5 minutes for the manifest to appear on arweave.net. If it times out, the site is still live from nginx. Once arweave.net has the data, finish the ARNS update with:

cd ~/av/prj/archerships.com
node ~/av/bin/archerships/deploy-website.mjs --set-arns <manifestTxId> --yes

The manifest txId is printed by the deploy script when it uploads the manifest.

inject-arweave-links.py runs automatically after a --new-files deploy. To run it manually (e.g., after a --set-arns or --full deploy):

python3 ~/av/bin/archerships/inject-arweave-links.py essays/YYYY-MM-DD-slug.html

The script polls arweave.net until the txId resolves, then injects a footer into the HTML with both a direct Arweave link and an ARNS link, and rsyncs the patched file to the gateway. Visitors can use these links to retrieve the content even if archerships.com goes offline.


Step 7: Cross-post to other platforms

hydra-publish YYYY-MM-DD-my-post-title \
    --platforms nostr,substack,facebook,twitter-article

hydra-publish reads the .md frontmatter for metadata, publishes to each platform, and records the resulting URL and ID back into published_at in the frontmatter.

For long-form posts, always use twitter-article (not twitter) – Twitter Articles display the full essay; a tweet would only carry the title and link.

Platform-specific notes:


Verification

  1. Confirm the post loads at https://archerships.com/essays/YYYY-MM-DD-slug.html.
  2. Check the footer has “Permanently archived on Arweave” links (added by inject-arweave-links.py once Arweave resolves – may take up to an hour).
  3. Verify images render correctly with no broken links.
  4. Confirm the post appears in https://archerships.com/feeds/all.xml.
  5. Confirm published_at in the frontmatter has entries for each platform posted to.

Want to stay in touch?

If you’d like to support my work:

If there is a topic you’d like me to cover, please let me know!

Questions, comments, and suggestions are welcome.