changelog-to-props.mjs defaulted
statsBefore/statsAfter to {0,0,0} and only filled them from a
Skill count: 197 → 199 regex that release-please changelogs never emit. That script also
feeds release-video.yml, so the zeros reached the rendered release videos, not just the
announce payload.Toggle the fix
Same input changelog, both code paths. These are the real values produced locally.
statsBefore: {"skills":0,"agents":36,"hooks":0}
statsAfter : {"skills":0,"agents":36,"hooks":0}
statsBefore: {"skills":105,"agents":36,"hooks":216}
statsAfter : {"skills":105,"agents":36,"hooks":216}
Why the manifest, and not a filesystem walk
Several sources were plausible. Only one is already CI-gated.
| Source | Verdict | Reason |
|---|---|---|
manifests/ork.json description |
chosen | Already asserted against the filesystem by
bin/validate-counts.sh, so a green CI makes it correct by construction.
One JSON read, no shell-out. |
| filesystem walk | no | Correct, but duplicates the counting rules (skip
shared/, exclude README/INDEX/CONTRIBUTING, hooks need
bin/count-hooks.sh). Two implementations drift. |
shared-data.ts TOTALS | no | Right numbers, but it is TypeScript and this is a plain
.mjs script. Reading it means regexing a generated TS file. |
| the changelog | no | The original approach. Release-please never writes a count delta, which is the entire bug. |
The fallback that was deleted, not ported
agents looked correct the whole time. It was luck.
// removed in this PR const allAgentMentions = [...fullText.matchAll(/(\d+)\s+agents/g)]; // ...count frequency, take the most frequent value >= 20 landscape.statsBefore.agents = count; // guessed a CURRENT total landscape.statsAfter.agents = count; // from HISTORICAL prose
It scanned the whole changelog and picked the most common "N agents" mention. It returned 36, which is right, but only because the agent count has been stable for months. One release that changed the count would have produced a stale number with no signal that anything was wrong.
Payload diff
{
"repo": "yonatangross/orchestkit",
"version": "10.0.0",
"release_url": "https://github.com/…",
"notes_markdown": "…",
"highlights": [],
"channels": ["twitter", "devto", "hashnode", "substack", "threads"],
"dry_run": false
}
{
"repo": "yonatangross/orchestkit",
"version": "10.0.0",
"release_url": "https://github.com/…",
"notes_markdown": "…",
"highlights": [],
"channels": ["twitter", "devto", "hashnode", "substack", "threads"],
"account": "yonyon2ai",
"dry_run": false
}
The field is inert until the platform reads it — an unknown key is ignored server-side. Shipping this half first records the intent and makes the platform change a one-sided edit.
Why state it rather than inherit it
| Property | Rationale |
|---|---|
| explicit, not inherited | A default that lives in the receiver's credential configuration is invisible from this repo. Naming the identity in the payload makes the intent reviewable in the diff. |
| fail loud, not soft | An unknown account value should be rejected rather than silently falling back. A silent fallback on this particular field publishes under the wrong identity. |
| overridable | ANNOUNCE_ACCOUNT is an env var, so a
different caller can select a different identity without a code change. |
What the latest prerelease would actually have announced
Composed locally from the real release. This is the complete announceable content.
highlights: 1
[changed] "mcp: drop the last ork-elicit leftover
from the esbuild header"
notes_markdown: 381 chars of release-please output
compare links, commit SHAs, issue refs
| Against the observed formula | |
|---|---|
| one capability, pain-first | no a leftover removal, phrased as internal jargon |
| a number that reframes | no no number at all |
| means something to a reader | no "ork-elicit leftover" is meaningless outside this repo |
| worth 1 of 33 posts | no 33 of these would train followers to ignore the feed |
The gate, verbatim
if [ "$EVENT_NAME" = "release" ] && semver_is_prerelease "$VERSION"; then skip=true echo "::notice::$VERSION is a prerelease — not announcing."
33 alphas, 33 green runs, 0 drafts. The workflow reports
success because skipping is the correct outcome. workflow_dispatch bypasses
it, so a manual dry run still exercises the whole path. The first real announce is the v10.0.0
GA cut.