| name | fs-release-and-publish |
| description | Ship founder-skills safely on a live-publishing master with no CI: run the executable pre-push gate (both pytest suites, node glob test, JSON parse, sanitization grep, optional URL sweep), bump plugin versions, handle installed-cache desync, and coordinate the sibling catalog site. Use when a founder-skills maintainer says "I'm about to push founder-skills", "release this founder-skills change", "publish the new plugin in this repo", "run the founder-skills pre-push gate", "bump this plugin's version", "why don't installed users see my change", or "update the site cards". If you are NOT working inside founder-skills, load the public skill-release-gate plugin instead — it is the generic version of this gate. |
fs-release-and-publish
Announce at start: "Using fs-release-and-publish — running the local pre-push gate before anything touches master."
Why this skill exists
This repo has no CI (standing org-wide rule since 2026-06-19: GitHub Actions billing-blocked plus operator preference — never author Actions workflows) and a live-publishing master: every push to master is instantly installable via the plugin marketplace and redeploys the GitHub Pages site from master:/docs (live at https://craigm26.github.io/founder-skills/, verified 200 on 2026-07-02). There is no staging. The mandatory LOCAL pre-push gate below is operator-approved standing policy (2026-07-02) and is the only thing standing between a mistake and the public.
Terms used once, defined once:
| Term | Meaning |
|---|
| plugin | A directory under plugins/<name>/ with SKILL.md + .claude-plugin/plugin.json (see fs-plugin-anatomy) |
| marketplace | .claude-plugin/marketplace.json at repo root; lists every plugin (15 as of 2026-07-02: 12 committed + 3 uncommitted skill-*); installers read it from master |
| installed cache | Per-machine copy at ~/.claude/plugins/cache/founder-skills/<plugin>/<version>/ created when a user installs (environment-specific path; this is the actual mechanism, not a convenience) |
| sanitization gate | Zero-hit grep over plugins/ for private names/keys/paths — doctrine hard gate for anything public (see fs-doctrine-and-honesty) |
| sibling site | github.com/craigm26/claude-skills-site — the separate catalog/portfolio repo (branch main, publishes https://craigm26.github.io/claude-skills-site/, verified 200 on 2026-07-02); must be coordinated manually |
The pre-push gate (executable)
The gate is a real script in this skill, not a checklist:
.claude/skills/fs-release-and-publish/scripts/pre-push-gate.sh
.claude/skills/fs-release-and-publish/scripts/pre-push-gate.sh --urls
Exit 0 = safe to push. Nonzero = do not push; the summary lists what failed.
| # | Gate | What it runs | Why |
|---|
| 0 | venv bootstrap | Creates $FS_VENV (default ~/venvs/founder-skills) + installs pytest if missing | Host Python 3.13.5 is PEP-668 externally managed; bare python3 -m pytest fails (defect ledger #2) |
| 1 | pytest market-validation | <venv>/python -m pytest -q tests/ in plugins/market-validation | 6 tests |
| 2 | pytest build-options | same, in plugins/build-options | 9 tests (2 of these failed on every fresh clone for ~4 weeks while master was live, 2026-06-04 → 2026-07-02 — defect #1) |
| 3 | node JS suite | node --test 'tests/js/*.test.mjs' in plugins/market-validation | 5 tests. GLOB form only — the directory form node --test tests/js/ fails with MODULE_NOT_FOUND on Node v24 (defect #3) |
| 4 | JSON + source dirs | json.load on marketplace.json + every plugin.json (15 as of 2026-07-02); every marketplace source dir exists | A malformed manifest breaks every installer at once |
| 5 | sanitization grep | Zero hits, case-insensitive, over the public dirs (plugins/ docs/ README.md .claude-plugin/, excluding docs/superpowers) for reservoir|sk_live|sk_test|cfut_|/home/craigm26|castor-dash|/opt/robot-md|/etc/robot-md — the doctrine scope (fs-doctrine-and-honesty Rule 3), which extends the plugins/-only pattern in docs/superpowers/plans/2026-06-11-publish-private-six.md repo-wide | Doctrine hard gate; the repo is public and docs/index.html + marketplace.json ship too |
| 6 | URL sweep (--urls only) | curl every unique https?:// URL in plugins/ (placeholder hosts example.com/localhost/x excluded as fixtures); 2xx/3xx pass, 401/403/405/429 WARN (bot-blocking — verify manually), else FAIL | No-oversell doctrine: claims trace to live URLs. Last full-repo URL verification before this script was 2026-06-11 (3 weeks stale) |
Real recorded output (run 2026-07-02, HEAD 2e4c9dd + the uncommitted working tree: template rescue + 3 skill-* plugins + their marketplace registration)
== founder-skills pre-push gate ==
repo: /home/craigm26/projects/craigm26/founder-skills
-- gate: pytest market-validation
6 passed in 0.23s
PASS pytest plugins/market-validation
-- gate: pytest build-options
9 passed in 0.11s
PASS pytest plugins/build-options
-- gate: node --test (market-validation js suite)
ℹ tests 5 / pass 5 / fail 0
PASS node --test plugins/market-validation/tests/js
-- gate: JSON parse + marketplace source dirs
checked 16 JSON files + 15 marketplace source dirs
PASS JSON parse + source dirs
-- gate: sanitization grep over public dirs (plugins/ docs/ README.md .claude-plugin/)
PASS sanitization grep (zero hits)
-- gate: curl URL sweep over plugins/ (network required)
PASS URL sweep (22 unique URLs)
== summary ==
ALL GATES PASSED — safe to push.
Notes: gate 2 passes only because the matrix.template.html rescue is in the working tree (uncommitted as of 2026-07-02) — on a fresh clone of 2e4c9dd it fails 2/9, which is exactly the point of running the gate. On a clean checkout of 2e4c9dd (before the 3 skill-* plugins) gate 4 reports 13 JSON files + 12 source dirs and the URL sweep 20 URLs.
Release runbook (in order)
- Change control first. The work must already have a spec + checkbox plan per fs-change-control. This skill does not authorize skipping that; it gates the push at the end.
- Gitignore-swallow check (defect #1 class: a plugin
.gitignore line *.html silently dropped a core asset; only the mutable installed cache kept a copy):
git status --short
git check-ignore -v plugins/<name>/assets/* 2>/dev/null
- Run the gate (
--urls if you touched public claims, READMEs, or links).
- Manifest validation — necessary but NOT sufficient:
claude plugin validate .
This checks manifest schema only. It passed while build-options was shipping without its core asset. Never treat it as the gate.
- Version bump if any plugin's content changed (next section).
- Operator commits and pushes. Standing rule (2026-07-02): assistants run no mutating git commands in this repo; the operator reviews and pushes. Commit messages match the plan text verbatim (fs-change-control).
- Post-push: confirm the Pages deploy (
curl -sI https://craigm26.github.io/founder-skills/ | head -1), refresh your own installed cache (/plugin marketplace update founder-skills inside Claude Code), and run the site follow-on below if plugin count or capabilities changed.
Version-bump procedure
Versions live in exactly one place per plugin: plugins/<name>/.claude-plugin/plugin.json → "version". The marketplace.json entries carry no version field (verified 2026-07-02), so no second file to sync inside this repo.
- Precedent (commit 93d91a2, 2026-06-11): the 0.1.0 → 0.2.0 bumps were made in the SAME commit as the SKILL.md capability changes they described. Follow that: content change and bump travel together.
- As of 2026-07-02 versions are mixed: effort, fable-loop-design, fable-orchestrated-feature-dev, fable-org-audit, fable-repo-audit, tasks are 0.2.0; the other nine (including the 3 uncommitted skill-* plugins) are 0.1.0. There is no repo-wide version.
- Always bump when content changes. The installed cache is keyed by version directory (
~/.claude/plugins/cache/founder-skills/<plugin>/<version>/, layout verified 2026-07-02). Whether a marketplace update refreshes a cache entry whose version did NOT change is UNVERIFIED — bumping sidesteps the question.
Installed-cache desync (know this before you push)
- Installs are snapshots. The operator's own cache equals HEAD 2e4c9dd, installed 2026-06-24. Every push silently desyncs every installed user until they run
/plugin marketplace update founder-skills — there is no push notification and no telemetry (defect #8 established that: the emit_atlas→emit_market_map rename had no way to know who broke).
- The cache is mutable at runtime: a Claude session regenerated the missing build-options template inside the cache on 2026-06-24, which masked the breakage for its final 8 days (2026-06-24 → the 2026-07-02 rescue; the repo had shipped broken since 2026-06-04). Never use the cache as evidence that the repo is complete; test from
git ls-files / a fresh clone. Full story: fs-failure-archaeology.
- After any push, refresh your local cache immediately so you are testing what users get, not what you remember.
Sibling-site coordination + site-card follow-on for new plugins
Two sites describe this repo and only one of them lives in it:
| Site | Source | Deploys | Coordination |
|---|
| founder-skills site | this repo, docs/index.html | automatically on push to master | edits ride the same pre-push gate |
| catalog site | separate repo claude-skills-site (env-specific checkout: /home/craigm26/projects/craigm26/claude-skills-site, branch main, clean at 01aa2b6 on 2026-07-02) | on push to its main | fully manual — nothing reminds you |
When a new plugin ships, the follow-on protocol is:
- Write a site-edit spec first — site changes are change-controlled like everything else (fs-change-control); wording and positioning rules live in fs-site-and-positioning.
docs/index.html: add a <div class="card"><h3>name</h3><span class="cost">…</span>…</div> inside the correct group's .cards grid; update every hard-coded plugin count (hero, meta description, section 05, install loop — grep for the number before assuming you found them all). The marketplace.json description was bumped to "15 skills" with the 2026-07-02 skill-* registration; the site and README still say "twelve" — that count decision (add cards + bump, or document the 3 as uncarded) is a mandatory spec'd follow-on of the same commit (see fs-site-and-positioning step 4).
- Catalog site: add the matching card, update its count stats ("12 installable today" as of 2026-07-02) and install-all loop.
- Do not propagate the existing oversell (defect #6: "~0 tokens" chips and "runs itself weekly" are live drift violating the repo's own no-oversell doctrine). New cards use honest cost copy; fixing the old cards is a documented follow-on, not a side effect of your push.
- Push order: this repo first (plugin must be installable before either site claims it), then the catalog site.
When NOT to use this skill
- Creating or scaffolding a new plugin (add-skill.sh flags, plugin.json generation, symlink discipline) → load fs-skill-authoring. This skill only gates the moment it goes public.
- Classifying a change, writing the spec/plan → fs-change-control. Nothing here routes around it.
- Rebuilding the environment, extending the test suites, debugging a red suite → fs-toolchain-and-tests. This skill only runs the suites.
- Actually writing/redesigning site copy or positioning → fs-site-and-positioning. This skill only tells you WHEN both sites must move.
- What a plugin directory must contain → fs-plugin-anatomy.
- Understanding why these gates exist (past failures) → fs-failure-archaeology.
Common mistakes | Fix
| Mistake | Fix |
|---|
"claude plugin validate passed, ship it" | Schema-only. Run pre-push-gate.sh; validate is step 4 of 7, not the gate |
python3 -m pytest on the host | PEP-668 failure. The gate bootstraps ~/venvs/founder-skills; use it |
node --test tests/js/ | MODULE_NOT_FOUND. Quoted glob only: node --test 'tests/js/*.test.mjs' |
Trusting git status clean = repo complete | .gitignore can swallow new assets silently; run the git check-ignore check on every new asset |
| Pushing content changes without a version bump | Installed caches are version-keyed; bump in the same commit as the change |
| Editing docs/index.html cards and calling it done | The catalog site is a separate repo and will now be wrong; run the follow-on protocol |
Assistant running git push | Forbidden standing rule; operator pushes |
Skipping --urls after editing READMEs/claims | URL verification is 3+ weeks stale (2026-06-11); the sweep is cheap (22 URLs, ~30 s) |
Provenance and maintenance
All facts verified 2026-07-02 against /home/craigm26/projects/craigm26/founder-skills at HEAD 2e4c9dd (+ uncommitted template rescue). Re-verify before trusting:
- Gate still green:
.claude/skills/fs-release-and-publish/scripts/pre-push-gate.sh --urls
- Test counts (6/9/5): the gate's own output prints them
- Plugin versions:
for f in plugins/*/.claude-plugin/plugin.json; do python3 -c "import json;d=json.load(open('$f'));print(d['name'],d['version'])"; done
- Marketplace has no version fields:
grep -c '"version"' .claude-plugin/marketplace.json (expect 0)
- Sanitization pattern origin (the gate extends it to the doctrine scope):
grep -n 'sk_live' docs/superpowers/plans/2026-06-11-publish-private-six.md
- Cache layout:
ls ~/.claude/plugins/cache/founder-skills/*/ (env-specific)
- Both sites live:
curl -s -o /dev/null -w '%{http_code}\n' https://craigm26.github.io/founder-skills/ https://craigm26.github.io/claude-skills-site/ (expect 200 200)
- Sibling checkout state:
git -C /home/craigm26/projects/craigm26/claude-skills-site status --short (env-specific)
- UNVERIFIED (open): whether
/plugin marketplace update refreshes a same-version cache entry; whether any external installer exists to be desynced (no telemetry)
References
scripts/pre-push-gate.sh — the executable gate (this skill directory)
docs/superpowers/plans/2026-06-11-publish-private-six.md — origin of the sanitization gate pattern (plugins/-only there; the gate script applies the wider doctrine scope)
scripts/add-skill.sh (repo root) — scaffolding, covered by fs-skill-authoring