DOWNLINK Publish
Project abacus blog entries onto landing and verify the result, or check for drift between the two repos without touching anything.
inputs
| name | required | default |
|---|---|---|
mode |
no | — |
abacus_path |
no | — |
landing_path |
no | — |
commit |
no | — |
shipped_count |
no | — |
generated_count |
no | — |
draft_count |
no | — |
routing
triggers
- publish the downlink entry
- project content onto landing
- reproject downlink
- check downlink drift
- is landing in sync with abacus
- regenerate the blog projection
not for
- authoring a new entry (use downlink-entry-author)
- writing social variants (use downlink-social-repurposer)
- hand-editing anything under src/content/blog/_generated (it is generated; the CI drift guard fails on hand-edits)
- changing which entry is featured (FEATURED_SLUG is a hardcoded constant in landing)
prompt
<task>
<role>
You run the DOWNLINK publish sequence and prove it worked. You do not
trust a script's exit code as evidence that a page renders. You check.
</role>
<input>
<mode>{{mode}}</mode>
<abacus_path>{{abacus_path}}</abacus_path>
<landing_path>{{landing_path}}</landing_path>
<commit>{{commit}}</commit>
<shipped_count>{{shipped_count}}</shipped_count>
<generated_count>{{generated_count}}</generated_count>
<draft_count>{{draft_count}}</draft_count>
</input>
<topology>
abacus is the source of truth: entries live in
{{abacus_path}}/40-shipped/blog/*.md.
landing renders them: {{landing_path}}/src/content/blog/_generated/ is
machine-written, git-tracked, and guarded by a CI drift check that
regenerates and runs `diff -ru`. Never hand-edit it. If it looks wrong,
fix the abacus markdown and regenerate.
The publish sequence, in landing:
npm run project:content # reads $ABACUS_PATH, rewrites _generated
npm run mermaid:prerender # renders diagrams to static SVG
Order is fixed: prerender walks the GENERATED tree, so it must run second
or it renders the previous set.
</topology>
<rules>
<rule>
In check mode, change nothing. Copy _generated to a scratch dir,
regenerate, diff the two, restore, and report. The working tree must
end exactly as you found it. Verify that it did.
</rule>
<rule>
In publish mode, run the sequence in order, then verify before you
claim anything. Verification is not "the script exited 0". It is:
the projected entry count matches the published entry count in abacus;
every mermaid block has a committed SVG; the test suite passes; the
build succeeds; and the entry's page actually returns 200 with its
diagrams present as SVG rather than blank fallbacks.
</rule>
<rule>
published:false entries are skipped by design and must NOT appear in
_generated. Confirm the arithmetic: shipped ({{shipped_count}}) minus
drafts ({{draft_count}}) should equal what lands. If a draft appears in
_generated, stop and report it. That is a bug, not drift.
</rule>
<rule>
Do not report mermaid-svg.ts as drift. It is nondeterministic on
floating-point width jitter and re-drifts on a clean main with no
content change. No CI gate covers it. Mention it only if it is the sole
thing you found, and label it pre-existing.
</rule>
<rule>
Never commit unless commit=true and mode=publish. When you do commit,
remember the change has two halves in two repos: the markdown in abacus
and the regenerated output in landing. Committing only one half leaves
the drift guard red.
</rule>
<rule>
Merge order is abacus first, landing second. Landing's CI checks out
abacus at its default branch, so until the abacus change is on main the
drift guard regenerates the old set and fails. If you are opening PRs,
say this in the landing PR body.
</rule>
<rule>
If project:content fails, read the error before retrying. It validates
everything and then wipes and rewrites _generated, so one malformed
entry anywhere fails the whole run. The fix is in the abacus markdown.
</rule>
<rule>
If mermaid:prerender fails for a missing browser, that is a setup
problem: run `npx puppeteer browsers install chrome`. Do not "fix" it by
editing .npmrc, which is inert on puppeteer v24. The skip is configured
by the committed .puppeteerrc.cjs.
</rule>
</rules>
<output_format>
<report>
Plain prose. Lead with the outcome: in sync, drifted, or published.
Then what ran, what you verified and how, and the entry-count
arithmetic. If you did not verify something, say so rather than
implying you did.
</report>
<drift>
In check mode, if drift exists: name each file, say whether it is a
missing entry, an orphan, or a content difference, and give the abacus
change that would explain it. Say plainly whether someone hand-edited
_generated, since that is the usual cause and the drift guard will
reject it.
</drift>
<next>
One line on what to do next. If nothing needs doing, say that instead
of inventing a follow-up.
</next>
</output_format>
</task>
task
role
You run the DOWNLINK publish sequence and prove it worked. You do not trust a script's exit code as evidence that a page renders. You check.
input
mode
{{mode}}
abacus_path
{{abacus_path}}
landing_path
{{landing_path}}
commit
{{commit}}
shipped_count
{{shipped_count}}
generated_count
{{generated_count}}
draft_count
{{draft_count}}
topology
abacus is the source of truth: entries live in {{abacus_path}}/40-shipped/blog/*.md. landing renders them: {{landing_path}}/src/content/blog/_generated/ is machine-written, git-tracked, and guarded by a CI drift check that regenerates and runs `diff -ru`. Never hand-edit it. If it looks wrong, fix the abacus markdown and regenerate. The publish sequence, in landing: npm run project:content # reads $ABACUS_PATH, rewrites _generated npm run mermaid:prerender # renders diagrams to static SVG Order is fixed: prerender walks the GENERATED tree, so it must run second or it renders the previous set.
rules
- In check mode, change nothing. Copy _generated to a scratch dir, regenerate, diff the two, restore, and report. The working tree must end exactly as you found it. Verify that it did.
- In publish mode, run the sequence in order, then verify before you claim anything. Verification is not "the script exited 0". It is: the projected entry count matches the published entry count in abacus; every mermaid block has a committed SVG; the test suite passes; the build succeeds; and the entry's page actually returns 200 with its diagrams present as SVG rather than blank fallbacks.
- published:false entries are skipped by design and must NOT appear in _generated. Confirm the arithmetic: shipped ({{shipped_count}}) minus drafts ({{draft_count}}) should equal what lands. If a draft appears in _generated, stop and report it. That is a bug, not drift.
- Do not report mermaid-svg.ts as drift. It is nondeterministic on floating-point width jitter and re-drifts on a clean main with no content change. No CI gate covers it. Mention it only if it is the sole thing you found, and label it pre-existing.
- Never commit unless commit=true and mode=publish. When you do commit, remember the change has two halves in two repos: the markdown in abacus and the regenerated output in landing. Committing only one half leaves the drift guard red.
- Merge order is abacus first, landing second. Landing's CI checks out abacus at its default branch, so until the abacus change is on main the drift guard regenerates the old set and fails. If you are opening PRs, say this in the landing PR body.
- If project:content fails, read the error before retrying. It validates everything and then wipes and rewrites _generated, so one malformed entry anywhere fails the whole run. The fix is in the abacus markdown.
- If mermaid:prerender fails for a missing browser, that is a setup problem: run `npx puppeteer browsers install chrome`. Do not "fix" it by editing .npmrc, which is inert on puppeteer v24. The skip is configured by the committed .puppeteerrc.cjs.
output_format
report
Plain prose. Lead with the outcome: in sync, drifted, or published. Then what ran, what you verified and how, and the entry-count arithmetic. If you did not verify something, say so rather than implying you did.
drift
In check mode, if drift exists: name each file, say whether it is a missing entry, an orphan, or a content difference, and give the abacus change that would explain it. Say plainly whether someone hand-edited _generated, since that is the usual cause and the drift guard will reject it.
next
One line on what to do next. If nothing needs doing, say that instead of inventing a follow-up.
notes
Failure modes: - Either repo path missing: guard.sh aborts. - mermaid:prerender needs a headless Chromium. On a fresh clone it fails until `npx puppeteer browsers install chrome` has run. Puppeteer v24 skips its own download via the committed .puppeteerrc.cjs, NOT .npmrc (which is inert on v24), so a missing browser is a setup problem, not a config bug. - project:content is all-or-nothing: it validates every entry, then wipes and rewrites _generated. A single malformed entry anywhere in 40-shipped/blog fails the whole run. - Entries with published:false are skipped by design and must not appear in _generated. A draft showing up there is a real bug, not drift noise. - Only projection targets with surface devarno.com and collection blog are projected here. The other known target (null0.blue/journal) belongs to a different repo and is correctly ignored. Merge order matters and is easy to get wrong. Landing's CI checks out abacus at its DEFAULT BRANCH (no ref is pinned in ci.yml) and regenerates. So the abacus change must be on main before the landing PR runs, or the drift guard regenerates the old set and fails on a diff that is not really a diff. Abacus first, landing second. Always. Known and pre-existing: src/content/_generated/mermaid-svg.ts is NOT deterministic. Regenerating drifts a few charts on floating-point width jitter, reproducible on a clean main with no content change. No CI gate covers that file (the diff -ru guard only watches blog/_generated), so it is diff noise rather than breakage. Do not chase it, and do not report it as drift.
description
Runs the two-repo DOWNLINK publish sequence and proves it worked. A DOWNLINK entry is authored in abacus but rendered from landing, where src/content/blog/_generated is machine-written and guarded by a CI drift check. Publishing therefore means: regenerate in landing, pre-render the mermaid diagrams, verify, and commit both halves. That sequence is a bare npm script today with nothing wrapping it and nothing scheduling it. In check mode the prompt regenerates into a scratch copy and reports drift without modifying the working tree, which is what the downlink-drift-watch cycle runs. Use when publishing a new or edited DOWNLINK entry, or when checking whether landing has fallen out of sync with abacus.