Implementation Plan

Publish a clean main branch from a develop workspace

todo
2026-06-06 agentics feature

Split the agentics repo into a develop branch for day-to-day work and a clean main branch (kept as the GitHub default) that carries only the plugin distribution — so every /plugin install pulls stripped plugin files directly from the default branch while docs, scripts, and development live on develop, automatically republished to main on every push.

Implement Read and implement all steps in the plan at docs/plans/publish-clean-main-from-develop-branch.html — Split repo into a develop dev branch and a clean main distribution branch
Run as workflow — launch parallel subagents
Run a workflow to implement the plan at docs/plans/publish-clean-main-from-develop-branch.html — Split repo into a develop dev branch and a clean main distribution branch
File publish-clean-main-from-develop-branch.html
Path docs/plans/publish-clean-main-from-develop-branch.html
Acceptance criteria 0 / 9 done

Context

The agentics repo is both the development workspace and the install source. .claude-plugin/marketplace.json registers 12 active plugins via git-subdir sources whose url points at https://github.com/shawn-sandy/agentics.git. When a user installs, Claude Code sparsely clones the plugin's path subtree from the repo's default branch — but that branch carries a lot that has no business in an install: a 35 KB root README.md, docs/, examples/, scripts/, tests/, .playwright-mcp/, a session .png, CLAUDE.local.md, .DS_Store files, and top-level markdown (ROADMAP.md, SECURITY.md, SOCIAL.md).

The sibling plan build-clean-plugin-dist.html solves this by mirroring the marketplace into a separate clean repo (shawn-sandy/agentics-kit) and rewriting each source.url. This plan takes a branch-based approach in the same repo instead: main stays as the GitHub default but is replaced with a clean, stripped plugin distribution, while a new develop branch holds the full workspace where all dev work and PRs happen. Because main remains the default branch, git-subdir sources need no ref field at all — installs naturally resolve from the default branch and get clean files. No second repo, no source.url rewrite, and no ref pins needed.

Decisions locked in clarification: branch model = developers push to develop first (all feature branches and PRs target develop), and only develop can be merged/pushed to main — no direct pushes, no PRs from feature branches to main. A GitHub Action rebuilds and republishes main on every push to develop, orphan-replacing main's tree with the freshly built clean output (main carries distribution snapshots, not dev history). main stays as the GitHub default so installs resolve from it automatically. A key advantage over the separate-repo plan: publishing stays in-repo, so the default GITHUB_TOKEN can push to main — no cross-repo PAT required. An additional advantage over flipping the default: no user migration needed — existing installs keep resolving from main seamlessly.

Files to Modify

agentics/
  • .github/workflows/
    • publish-dist.yml new rebuild + publish main on push to develop
    • version-guard.yml modified retarget PR branch filter main → develop
    • update-readme.yml modified ensure it writes to develop, not main
  • .claude-plugin/marketplace.json modified strip removed[] for clean dist; no ref pins needed
  • scripts/build-dist.mjs new manifest-driven clean builder + check/publish
  • .claude/rules/marketplace.md modified main is generated; never hand-edit
  • README.md modified branch model & distribution section
  • CLAUDE.md modified develop-default; never commit to main
  • main (branch) generated clean plugin distribution snapshot

Diagram

Publish pipeline — develop to clean main
Source / dev
develop
developers push here first — all feature branches & PRs target develop
CI
.github/workflows/publish-dist.yml
checkout → setup-node → --check → --publish (GITHUB_TOKEN)
Build
scripts/build-dist.mjs
reads plugins[] · copies KEEP · drops cruft & removed[] · strips to clean dist
Clean dist (default branch)
main
manifest + component dirs + README + LICENSE — no docs/ or cruft
Users
/plugin install <name>@agentics-kit
clean tree resolved from main (the default branch)
What lives on each branch
develop — full workspace
  • docs/ · examples/
  • scripts/ · tests/
  • *.local.md · ROADMAP/SECURITY/SOCIAL.md
  • full kit/plugins/* (with scratch)
  • all dev CI workflows
main — kept (clean, default)
  • .claude-plugin/marketplace.json
  •   no ref needed (default branch)
  • kit/plugins/<name>/ stripped
  • slim README.md · LICENSE
stripped from main
  • docs/ · examples/ · tests/
  • *.local.md · .DS_Store · *.png
  • .playwright-mcp/
  • ROADMAP/SECURITY/SOCIAL.md
  • removed[] plugins (×6)

Steps

1
todo Create the develop branch from main and configure PR targeting
develop must carry the entire current workspace before main is ever stripped. Tag the pre-split commit (e.g. git tag pre-dist-split) as a safety net so the full history stays reachable. Main stays as the GitHub default so that /plugin install resolves from it without any ref pin — but PRs and dev work must target develop going forward.
Verify
git checkout main && git pull && git checkout -b develop && git push -u origin develop succeeds; gh repo view --json defaultBranchRef -q .defaultBranchRef.name still returns main; git diff main develop --stat is empty (identical trees at split time); git tag pre-dist-split exists.
2
todo Enforce the develop-first workflow and retarget open PRs
Developers push to develop first — all feature branches and PRs must target develop, never main. Only the CI publish workflow writes to main. Retarget any open PRs from main to develop and communicate the new branching model to contributors.
Verify
gh pr list --base main --state open returns no results (all retargeted to develop); commit lands on develop.
3
todo Write scripts/build-dist.mjs — a manifest-driven clean builder
A deterministic build from develop is the only writer of main, guaranteeing the clean branch never drifts. Mirror the existing merge-marketplace.mjs convention (bare ESM, run via node, no package.json). Enumerate plugins by reading marketplace.json plugins[] (never by globbing); copy only a KEEP allowlist (.claude-plugin, commands, skills, agents, hooks, hooks.json, templates, README.md, CHANGELOG.md, LICENSE) per plugin into an OUT_DIR; drop a DROP denylist (docs, *.local.md, .DS_Store, *.png, .playwright-mcp); emit a clean root (marketplace.json with removed[] stripped, a slim README.md, copied LICENSE).
Verify
node scripts/build-dist.mjs --list prints exactly the 12 active plugin names and omits the 6 in removed[]; after node scripts/build-dist.mjs, find <OUT_DIR> -name '*.local.md' -o -name '.DS_Store' -o -path '*/docs/*' returns nothing.
4
todo Add --check and orphan-replace --publish modes to the build script
--check walks the built output and exits non-zero if any DROP pattern leaked (a CI tripwire against bloat). --publish implements the chosen "orphan / replace each publish" model: it builds the clean tree, replaces main's working tree wholesale with that output as a single fresh commit referencing the source develop SHA, and updates main (via git worktree + a clean checkout, or an orphan commit) so deleted plugins disappear downstream. Support --dry-run so the commit can be previewed without pushing.
Verify
On a clean build, node scripts/build-dist.mjs --check; echo $? prints 0; temporarily adding docs to KEEP, rebuilding, and rerunning --check exits non-zero naming the offending path (then revert). node scripts/build-dist.mjs --publish --dry-run reports the single clean commit it would write to main without pushing.
5
todo Add .github/workflows/publish-dist.yml triggered on push to develop
"On every push to develop" keeps main in lockstep automatically. Steps: on: push: branches: [develop] plus workflow_dispatch; checkout develop, actions/setup-node, node scripts/build-dist.mjs --check (fail fast on leakage), then --publish to replace main's tree. Because the dist target is the same repo, set permissions: contents: write and use the default GITHUB_TOKEN — no cross-repo PAT. Add a concurrency group (publish-dist, cancel-in-progress) so rapid pushes don't race on main.
Verify
YAML lints clean (actionlint .github/workflows/publish-dist.yml); a workflow_dispatch run (or a test push to develop) completes green and writes a new clean commit to main; git ls-tree -r origin/main --name-only | grep -E 'docs/|\.local\.md|\.DS_Store' returns nothing.
6
todo Retarget dev workflows to develop and protect main as generated-only
Dev automation must run against develop (where all PRs target) and main must have exactly one writer (the publish-dist Action). version-guard.yml filters pull_request: branches: [main] — change to [develop]. Update update-readme.yml to commit to develop instead of main. claude-code-review.yml / claude.yml need PR base branch filter updated to develop. Add branch protection on main to block direct pushes and PRs — only the publish-dist Action (via GITHUB_TOKEN) can write to main.
Verify
grep -n 'branches:' .github/workflows/version-guard.yml shows [develop]; opening a test PR against develop triggers the version-guard and Claude review workflows; no workflow other than publish-dist.yml pushes to main.
7
todo Document the branch model in README, CLAUDE.md, and the marketplace rule
Contributors and future Claude sessions must know: developers push to develop first, all PRs target develop, and only develop is published to main via CI. Main is a generated artifact (clean dist + default branch) — never commit to main directly. Add a "Branch model & distribution" section to README.md explaining the develop-first workflow. Include an "Existing Users" migration note: installed plugins are safe (local copies in ~/.claude/plugins/cache/), the marketplace self-heals on next /plugin marketplace update (one-time re-clone after the orphan-replace), and no action is required — but if users hit issues, they can remove and re-add the marketplace. Update CLAUDE.md's Git & Branches section to branch off develop and never commit to main. Note in .claude/rules/marketplace.md that main is generated — not hand-edited.
Verify
grep -ri 'develop' README.md CLAUDE.md .claude/rules/marketplace.md shows the new branch-model guidance in all three; CLAUDE.md's branch-creation command branches off develop; marketplace.md states main is generated and not hand-edited; README.md contains an "Existing Users" section with migration guidance.
8
todo Cut over: run the first publish and verify a clean end-to-end install
Proves the split works for real installs. Trigger publish-dist.yml (or run --publish once) to rebuild main clean, then in a fresh session register the marketplace and install a plugin — confirming the catalog and plugin files both resolve from main (the default branch) and contain only clean distribution files.
Verify
In a clean session, /plugin marketplace add shawn-sandy/agentics then /plugin install plan-agent@agentics-kit installs successfully; the installed tree contains no docs/, *.local.md, .DS_Store, or session PNGs; git ls-tree origin/main --name-only shows only the clean root set.

Acceptance Criteria

Verification

End-to-end: confirm gh repo view --json defaultBranchRef still reports main and that the pre-split tag exists. Run node scripts/build-dist.mjs --check locally (exit 0), then verify the tripwire by temporarily allowlisting docs and confirming a non-zero exit. Trigger publish-dist.yml via workflow_dispatch (or push a no-op commit to develop) and confirm it writes a fresh clean commit to main; then git ls-tree -r origin/main --name-only must show 12 plugin directories carrying only allowlisted files plus a root marketplace.json, slim README.md, and LICENSE — and zero cruft. Finally, in a clean session register shawn-sandy/agentics and install plan-agent (both resolve from main, the default branch), then list the installed files to confirm no docs/, *.local.md, or PNGs. develop must still hold the complete workspace, unchanged except for the new script, workflow, and docs.

Completion Checklist

Required

Completion Report

No items to report — all requirements met.

Next Steps

Add branch protection so only the publish Action writes main

Paste this prompt into Claude to execute this follow-up:

In the shawn-sandy/agentics repo, add GitHub branch protection on the main branch so that direct pushes and pull requests are blocked, and only the publish-dist.yml GitHub Action (running with the default GITHUB_TOKEN) can update it. Use the gh CLI (gh api) to configure a branch protection rule on main: block force-pushes from humans but allow the Actions bot, require no PRs (main is generated), and document the rule in .claude/rules/marketplace.md. Confirm a manual push to main is rejected while a publish-dist workflow run still succeeds.
Add a per-publish leak report and size diff to the build

Paste this prompt into Claude to execute this follow-up:

In scripts/build-dist.mjs (the branch-based clean-distribution build for the agentics marketplace), add a post-build summary that prints, per plugin, the source file count and byte size on develop versus the stripped count and byte size published to main, plus a total "bytes saved" line and an explicit list of any paths that matched the DROP denylist and were excluded. Drive the plugin list from .claude-plugin/marketplace.json plugins[]. Keep it dependency-free.
Wish List
Auto-generate release notes on each main publish Wish List

Speculative / blue-sky idea — not on the critical path. Paste into Claude when ready to explore:

Explore extending the agentics publish-dist pipeline so each push to develop that changes a plugin not only republishes the clean main branch but also drafts release notes: diff the new main tree against the previous main commit, group changes by plugin using marketplace.json, and open a GitHub Release (or a CHANGELOG entry) summarizing which plugins changed and their version bumps. Recommend whether this should live in build-dist.mjs or a separate workflow step, and outline the implementation.
Unresolved Questions
  • Should main be a true orphan branch or a rolling single-commit branch?
    For the agentics publish-dist pipeline, decide between two implementations of "orphan / replace each publish" for the main branch: (a) a true orphan branch with no parent (git checkout --orphan) force-pushed each publish, versus (b) a normal branch where each publish commits the fully-replaced clean tree on top of the previous main commit (no force-push, history grows but stays diffable). Weigh force-push safety for downstream consumers, diffability of releases, and CI simplicity, and recommend one. Then specify the exact git commands build-dist.mjs --publish should run.