Second pass: filled the empty skeleton (When-to-use, Inputs, Steps, Failure modes, Side effects, What-NOT) from the design archive (.workshop/.archive/2026-05-29-skill-private-dev-public-publish.md). 0.1.0 -> 0.2.0. Non-implementer subagent review found 3 findings, all fixed in this same increment: - Step 5 dev->pub copy had no meta-exclusion -> would leak .wiki/.tasks/CLAUDE.md into the PUBLIC fork. Added explicit meta-exclude + .gitignore backstop + git-status check, plus a 4th failure mode for the leak. - pub-folder origin was never established before Step 5 pushed to it -> Step 4 now clones the GitHub fork into pub (origin=fork, upstream=canonical). - Step 3 "same base" was unmechanized -> clone fork, add gitea remote, push base. Closes review task: all findings filed and resolved; no follow-ups needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.7 KiB
name, version, description
| name | version | description |
|---|---|---|
| private-dev-public-publish | 0.2.0 | Use when setting up or maintaining a publishable open-source port/fork that should be developed privately but published cleanly — messy development on a PRIVATE Gitea repo (with `.wiki/`+`.tasks/` inside), and a curated COPY of finished work into a PUBLIC GitHub fork that preserves upstream lineage (stays a fork, keeps attribution; GPL-clean). Triggers - «опубликовать форк/порт на гитхаб», «разработку держать приватно, релиз публичный», «приватный гитеа + публичный гитхаб», «publish a fork without exposing dev history», «curated publish to GitHub», «как правильно форкнуть open-source для публикации». NOT for purely-private projects, purely-public open development, or greenfield bootstrap (use project-bootstrap). |
private-dev-public-publish
Two-repo topology for a publishable open-source port/fork: develop messily on a private Gitea repo (with .wiki/ + .tasks/ inside it), then publish only curated, finished work to a public GitHub fork that stays a real fork of upstream — so attribution and lineage hold and the dev history (experiments, dead-ends) never goes public.
| Role | Where | Contents | Visibility |
|---|---|---|---|
| Dev | private Gitea repo | upstream base + all development + .wiki/+.tasks/+CLAUDE.md; messy history |
private |
| Publish | public GitHub fork | code only, curated clean commits, upstream lineage (stays a fork) | public |
You work in the Gitea (dev) folder; matured work is copied as files into the GitHub (pub) folder → one clean commit → push.
When to use
- You're porting/forking an open-source project and intend to publish it, but the development is exploratory (experiments, dead-ends, reverts) you don't want in public history.
- You want attribution and licence lineage to hold (the public repo must stay a real fork of upstream).
- You need a place for
.wiki/+.tasks/+CLAUDE.mdthat never ships publicly.
Why curated publication is legitimate (GPL/OSI): the licence requires the source of what you distribute (the release), not your development history. A curated publish is clean as long as the public repo (1) stays a fork of upstream (lineage = attribution) and (2) contains the complete buildable source of the release.
Inputs
- Upstream — the canonical project you're porting/forking (URL + the specific commit/tag that is your real base).
- Public target — GitHub account/org for the fork.
- Private dev host — Gitea repo (the primary working clone;
projects-metapoints here, not at GitHub).
Steps
- Establish provenance. Identify which upstream commit/fork is your real base. If history was lost, content-match the tree; find the author's PR/fork for the fix you're carrying so attribution is correct.
- Fork canonical upstream on GitHub (
gh repo fork) → the public showcase.upstreamremote = the original. Bring in needed third-party fixes viacherry-pick(preserves authorship) or merge. - Create the private Gitea dev repo on the same base: clone the GitHub fork locally, add the Gitea repo as a remote, and push the base there — this carries the upstream lineage into the private repo. Put meta (
.wiki/+.tasks/+CLAUDE.md) inside it. Trap: a global~/.config/git/ignore(core.excludesfile) may silently ignore.wiki//.tasks/→ add local!-negation lines in the repo's.gitignore. - Two local folders: dev (the Gitea private clone — your primary working copy) and pub (a clone of the GitHub fork; its
origin= your fork,upstream= canonical). - Publish: copy the code files dev→pub, excluding meta (
.wiki/,.tasks/,CLAUDE.md, and any private notes — these must never reach the public fork; also list them in the pub repo's.gitignoreas a backstop). Rungit statusin pub to confirm no meta is staged. Make one clean commit,push origin(github). Never reconstruct history — "copy" means lay files into the fork's tree.
Failure modes
- Public folder is no longer a fork (rootless snapshot) → attribution/lineage lost. Do NOT start history from scratch; "copy" = place files into the fork's tree, keep the fork relationship.
- Private meta copied into the public fork (
.wiki//.tasks//CLAUDE.mdleak) → dev internals exposed publicly. The dev→pub copy MUST exclude meta; keep those paths in the pub repo's.gitignoreand checkgit statusin pub before committing. - meta silently not committed in the private repo (global gitignore swallows
.wiki//.tasks/) → verify withgit check-ignore .wiki .tasks; add negation lines if matched. - Release without complete source in the public repo → GPL violation. The public release must be fully buildable from what's published.
Side effects
- Creates a public GitHub fork (outward-facing — visible to anyone).
- Creates a private Gitea repo and two local working clones.
- Touches git remotes,
gh/GitHub API, and potentially tokens — confirm before any push or repo-visibility change.
What NOT to do
- Don't use this for a purely-private project (no intent to publish) — no second repo needed.
- Don't use it for purely-public open development (nothing to hide) — a normal fork+dev on GitHub is enough.
- Don't use it for greenfield bootstrap of a new project with no upstream — that's
project-bootstrap. - Don't create a standalone meta repo — meta lives inside the private dev repo. A separate meta repo is only for when the dev repo itself is public.
- Don't publish by pushing dev history or by initializing a fresh repo — both break lineage.