Publishing
Publishing is handled by scripts/deploy.sh. It’s the last spot where a post that cleared both gates goes up to Blogger. Three credential files have to be in place for an actual upload to happen; without them it stops at the dry-run.
Three credential files
Section titled “Three credential files”An actual publish needs three files.
test -f .env # BLOG_ID_KO, BLOG_ID_ENtest -f scripts/credentials.json # Google OAuth clienttest -f scripts/token.json # issued access token.env holds the IDs of the Korean blog and the English blog. credentials.json is the OAuth client you got from the Google Cloud console, and token.json is the token issued after the first authentication. All three are planted in .gitignore per S1 and drop out of git tracking. Not leaving a token in plaintext in a body or log is S7.
If the three files aren’t in place, the publish script runs only through dry-run and preflight, then stops. It reports the missing file and does not invent a Blogger URL.
Check staging
Section titled “Check staging”Before publishing, look at the staging state first.
./scripts/deploy.sh stagingThe list of posts awaiting publish under _workspace/posts/new/ comes up. This is where you confirm which slug is ready to go. Before creating a new slug, check for collisions first.
find _workspace/posts _workspace/briefs -iname "*{slug}*" -printIf a staging post for the same slug already exists, continue from that file rather than creating a new one. Don’t overwrite posts the user has edited or unrelated dirty files.
Dry-run first
Section titled “Dry-run first”Before the actual upload, preview the result with a dry-run.
./scripts/deploy.sh publish-new --slug {slug} --dry-runThe dry-run shows which post goes to which blog and how the frontmatter is transformed, without actually uploading. The two gates (AI-smell, quality) are re-checked at this step too. If it fails, it gets blocked right at the dry-run.
Slug-scoped publishing
Section titled “Slug-scoped publishing”The actual publish is called with the slug named explicitly.
./scripts/deploy.sh publish-new --slug {slug}The reason for narrowing to a slug is to prevent the accident of unrelated staging posts going up together. Calling publish-new without a slug targets every post in staging. To put up just one, name the slug.
Verify after publishing
Section titled “Verify after publishing”Once the upload finishes, confirm it moved to archive and the metadata is planted.
./scripts/deploy.sh stagingfind _workspace/posts/ko _workspace/posts/en -iname "{slug}.md" -printrg -n "blogger_post_id|blogger_url" _workspace/posts/ko _workspace/posts/en -g "{slug}.md"On a successful publish, the post moves from posts/new/ to posts/, and blogger_post_id and blogger_url get filled into the frontmatter. These two fields aren’t planted in the staging file ahead of time. The script fills them in after a successful upload.
The Korean and English versions go as a pair
Section titled “The Korean and English versions go as a pair”One post goes up to the Korean blog (BLOG_ID_KO) and the English blog (BLOG_ID_EN) separately. Because the frontmatter’s hreflang_en and hreflang_ko point at each other, if only one publishes the hreflang cross-reference breaks. The Phase 3 ko/en parity verdict looks at this spot ahead of time. Both bodies and the hreflang have to mesh for a PASS to drop.
Republishing
Section titled “Republishing”When you fix an already-published post and put it back up, back up the prior version first per S3. Backing up before overwriting an output is the publish script’s promise. Use --skip-gate only in a republish situation that has to bypass the AI-smell gate. Don’t use it for a new post’s first publish.
The next section is marketing. It covers where marketing-director spreads the post across X, Reddit, and Hacker News after publishing, and the three axes by which AI-SEO gets Claude or ChatGPT to cite the post.