Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "github-release",
"version": "0.1.0",
"version": "0.2.0",
"description": "Create GitHub repositories for Agent Skills and release them right the first time: version gate, deterministic host packages, draft-first publishing, published-asset verification and a per-host install log.",
"author": {
"name": "Jesse Raber",
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "github-release",
"version": "0.1.0",
"version": "0.2.0",
"description": "Create GitHub repositories for Agent Skills and release them right the first time: version gate, deterministic host packages, draft-first publishing, published-asset verification and a per-host install log.",
"skills": "./skills/",
"author": {
Expand Down
Binary file added .github/social-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## 0.2.0 (draft, not released)

Portfolio and install lessons (R-023..R-035, R-038..R-040; evidence in the project's 2026-10-08 scan and install records):

- Gate: G0 release-lane-free (one release in flight; expected-main-SHA refusal); fresh clone outside synced folders; G8b executable-bit drift; G8c tag-kind consistency; G11 out-of-repo touch points (`check_versions.py --extra-root`, e.g. a wiki clone); G12 lightweight visual check before public pushes.
- Publish: connector/API pushes can alter bytes — compare pushed blob SHAs with `git hash-object` before the PR; never rename a branch with an open PR (replacement-PR procedure); `--force-with-lease` scope; delete the merged release branch with a recovery SHA after publishing.
- Host matrix: install channels per host (they drift independently; read installed files, never catalog caches); Microsoft Copilot split into Agent Builder vs chat attachment with the ZIP-root question documented open (W-015); Opal/Manus/Copilot sandboxes are review-only — no git work there.
- Record: install procedure (backup with manifest, file-for-file compare incl. zero extra files); `Channel` column in HOST_INSTALL_LOG; new `templates/HOST_INSTALL_CHECK_PROMPT.md` and `templates/NEXT_AGENT_RELEASE_PROMPT.md`.
- Loaded line now names the SKILL.md path, so checks can spot shadow copies.
- Repo setup: auto-delete head branches and social-preview/Website fields as defaults; `safe.directory` note for tool-owned clones.

## 0.1.0 (draft, not released)

- First version: modes Setup, Prepare, Gate, Publish, Verify, Record.
Expand Down
4 changes: 4 additions & 0 deletions HOST_INSTALL_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ Evidence: `observed` (agent saw it), `user-reported`, or a path to a screenshot

| Date (TZ) | Version | Asset (sha256 first 12) | Host / account | Action | Result | Displayed | Evidence | By |
|---|---|---|---|---|---|---|---|---|
| 2026-10-08 13:xx-14:22 EDT | 0.1.0 candidate | github-release-0.1.0-UNIVERSAL-skill.zip (7958b5b64ea5) | Codex desktop / local user skills | Install, fresh-session load and routing smoke test | loaded-smoke-pass; install accepted and 22/22 files matched; two earlier subprocess attempts hit an execution-helper setup error, but the next Codex turn loaded v0.1.0 and correctly routed standalone Codex to Universal | `Loaded GitHub Release v0.1.0` | observed; current chat T003 plus failed subprocess sessions 01a11cb2-4292-78d3-8de0-ea32a8175ed4 and 01a11cb2-e502-7e31-a498-d11fdef07400 | Codex session 185e6066-cbb8-4abd-9d1a-394bd0b4fcb5 |
| 2026-10-08 14:xx-14:28 EDT | 0.1.0 candidate | github-release-0.1.0-claude-code-plugin.zip (2d0bf67303c3) | Claude Code 2.1.223 / local user plugin | Install, enable, validate and routing smoke test | loaded-smoke-pass; install accepted, enabled, 26/26 cached files matched, marketplace validation passed, and authenticated routing smoke passed after an earlier expired-OAuth attempt | `Loaded GitHub Release v0.1.0`; selected `-claude-code-plugin` | observed; local CLI output, session T005 | Codex session 185e6066-cbb8-4abd-9d1a-394bd0b4fcb5 |
| 2026-10-08 ~16:2x EDT | 0.1.0 (published) | github-release-0.1.0-claude-code-plugin.zip (f7d0f6bfed75) | Claude Code / local user plugin via marketplace | Reinstall from published release asset (hash-verified vs SHA256SUMS), routing smoke test | loaded-smoke-pass; supersedes candidate-hash row above | `Loaded GitHub Release v0.1.0`; selected `-claude-code-plugin` | observed; CLI output, Claude session 2e2fbf7d | Claude session 2e2fbf7d-1c2d-47a7-82fd-16374ceac5f0 |
| 2026-10-08 ~16:2x EDT | 0.1.0 (published) | github-release-0.1.0-UNIVERSAL-skill.zip (cfffc4ea2d18) | Codex desktop / local user skills | Reinstall from published release asset (hash-verified vs SHA256SUMS) | accepted; 22/22 installed files match archive; behavioral smoke not run (needs a Codex session); supersedes candidate-hash row above | none shown | observed; file-hash comparison, Claude session 2e2fbf7d | Claude session 2e2fbf7d-1c2d-47a7-82fd-16374ceac5f0 |
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ An Agent Skill that teaches AI agents to set up skill repositories and release t
| Gemini Apps | `…-gemini-apps-only.zip` |
| Opal | `…-opal-only.zip` |

## The flow it enforces

```mermaid
flowchart LR
P[Prepare\ncandidate + checks] --> G[Gate\nG0-G12, read-only]
G -->|owner approves tag| T[Tag pushed\nworkflow builds DRAFT]
T --> V[Verify draft\nbytes == candidate]
V -->|owner approves publish| R[Published\nimmutable]
R --> H[Host installs\nlogged per channel]
```

## What it enforces

- Creating a repository, pushing, tagging, publishing, changing settings and installing into hosts are separate owner approvals.
Expand Down
2 changes: 1 addition & 1 deletion packaging/INSTALL-microsoft-copilot.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ This package has no PowerShell files. The Python helpers need only the standard

A successful upload proves only package acceptance. Test with a read-only request such as "Is this repository ready to tag?".

Expected loaded version: **GitHub Release v0.1.0**.
Expected loaded version: **GitHub Release v0.2.0**.
2 changes: 1 addition & 1 deletion packaging/INSTALL-universal.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Point the system prompt or context loader at `github-release/SKILL.md`.

> Is my skill repository ready to tag v1.0.0?

A loaded skill states **Mode: Gate** first and reports **Loaded GitHub Release v0.1.0**.
A loaded skill states **Mode: Gate** first and reports **Loaded GitHub Release v0.2.0 (SKILL.md at <path>)**.

## Requirements

Expand Down
12 changes: 12 additions & 0 deletions packaging/RELEASE_NOTES_v0.2.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# github-release 0.2.0

Hardening release from real multi-agent release history: a release-lane gate (one release in flight, expected-SHA refusal), connector byte-drift checks, fresh-clone and sandbox rules, per-host install channels with an install-check prompt template, an install procedure with backups and zero-extra-file verification, out-of-repo version checks (`--extra-root`), and a loaded line that names its own path. See CHANGELOG.md for the full list.

Verify downloads with `sha256sum -c SHA256SUMS.txt`.

## Publication boundary

Installed host copies are updated separately; this release does not change any host by itself.

---
Built by [Jesse Raber](https://jesseraber.net) · [YouTube](https://www.youtube.com/@Jesse_Raber)
31 changes: 22 additions & 9 deletions packaging/check_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import re
import sys

__version__ = '0.1.0'
__version__ = '0.2.0'
SEMVER = re.compile(r'^\d+\.\d+\.\d+$')
TEXT_SUFFIXES = {'.md', '.py', '.json', '.yml', '.yaml', '.txt', '.ps1', '.toml', '.cfg'}
SKIP_DIRS = {'.git', '__pycache__', 'node_modules', 'dist'}
Expand Down Expand Up @@ -75,25 +75,35 @@ def is_history(rel, patterns):
return any(fnmatch.fnmatch(rel, pat) for pat in patterns)


def check_previous(repo, cfg, previous, results):
pat = re.compile(r'(?<![\d.])v?' + re.escape(previous) + r'(?![\d])')
hits = []
for root, dirs, files in os.walk(repo):
def _scan_previous(base, label, pat, history, hits):
for root, dirs, files in os.walk(base):
dirs[:] = sorted(d for d in dirs if d not in SKIP_DIRS)
for f in sorted(files):
p = Path(root) / f
if p.suffix.lower() not in TEXT_SUFFIXES:
continue
rel = p.relative_to(repo).as_posix()
if is_history(rel, cfg['history']):
rel = p.relative_to(base).as_posix()
if history is not None and is_history(rel, history):
continue
try:
lines = p.read_text(encoding='utf-8').splitlines()
except (UnicodeDecodeError, OSError):
continue
for i, line in enumerate(lines, 1):
if pat.search(line):
hits.append('%s:%d: %s' % (rel, i, line.strip()[:120]))
hits.append('%s%s:%d: %s' % (label, rel, i, line.strip()[:120]))


def check_previous(repo, cfg, previous, results, extra_roots=()):
pat = re.compile(r'(?<![\d.])v?' + re.escape(previous) + r'(?![\d])')
hits = []
_scan_previous(repo, '', pat, cfg['history'], hits)
for er in extra_roots:
erp = Path(er)
if not erp.is_dir():
results.append(('FAIL', 'extra root not found (clone it or drop the flag): %s' % er))
continue
_scan_previous(erp, erp.name + '/', pat, None, hits)
if hits:
results.append(('FAIL', '%d mention(s) of previous version %s outside history files; '
'fix each or add the file to "history":' % (len(hits), previous)))
Expand All @@ -102,6 +112,7 @@ def check_previous(repo, cfg, previous, results):
results.append(('PASS', 'no mentions of previous version %s outside history files' % previous))



def check_workflow(path, results):
p = Path(path)
if not p.is_file():
Expand Down Expand Up @@ -132,6 +143,8 @@ def main(argv=None):
ap.add_argument('--repo', default='.')
ap.add_argument('--version', dest='new_version')
ap.add_argument('--previous')
ap.add_argument('--extra-root', action='append', default=[],
help='also scan this directory (e.g. a wiki clone) for previous-version mentions; repeatable')
ap.add_argument('--config')
ap.add_argument('--workflow')
ap.add_argument('-V', '--tool-version', action='store_true', help='print helper version')
Expand All @@ -158,7 +171,7 @@ def main(argv=None):
check_touch_points(repo, cfg, args.new_version, results)
check_notes(repo, args.new_version, results)
if args.previous:
check_previous(repo, cfg, args.previous, results)
check_previous(repo, cfg, args.previous, results, args.extra_root)
if args.workflow:
check_workflow(repo / args.workflow if not Path(args.workflow).is_absolute() else args.workflow, results)
failed = sum(1 for status, _ in results if status == 'FAIL')
Expand Down
2 changes: 1 addition & 1 deletion packaging/verify_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import sys
import zipfile

__version__ = '0.1.0'
__version__ = '0.2.0'
SUMS = 'SHA256SUMS.txt'


Expand Down
4 changes: 2 additions & 2 deletions skills/github-release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ name: github-release
description: Create GitHub repositories for Agent Skills and release them right the first time - version agreement, a pre-tag gate, deterministic host-specific packages (Claude app, Claude Code, Codex/ChatGPT, Microsoft Copilot, Grok, Gemini Apps, Opal, Antigravity, local models), draft-first publishing, verification of published assets against the local build, and a dated per-host install log. Use whenever an agent prepares, tags, publishes, re-packages or verifies a skill release, sets up a skill repository or release workflow, decides which ZIP a host needs, or records that a skill was uploaded or installed somewhere.
license: MIT
metadata:
version: "0.1.0"
version: "0.2.0"
repository: https://github.com/JesseRaber/github-release
---

# GitHub Release

A release is right the first time when the bytes you tested are the bytes people download, every host gets a package it accepts, and the record says which host has which version and how anyone knows.

Report as: **Loaded GitHub Release v0.1.0**. State the mode in the first line.
Report as: **Loaded GitHub Release v0.2.0 (SKILL.md at <path you loaded>)**. State the mode in the first line. The path lets install checks detect shadow copies and stale channels.

## Choose the mode

Expand Down
15 changes: 14 additions & 1 deletion skills/github-release/references/host-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,34 @@ Reference: `multi-agent-folder-cleanup` v1.6.1. On 2026-10-08 the author rebuilt
| `-opal-only` | Opal import | `SKILL.md` with only `name` + `description` frontmatter; top-level `references/*.md`; LF endings; **no folder entries** | everything else | observed: confirmed by import (repo CHANGELOG 1.5.0); per-file size rejection above ~48–79 KB observed 2026-10-03, approximate | 2026-10-06 |
| `-project-rules-optional` | add-on, any host | only if the skill ships optional add-on material | — | repo | 2026-10-06 |

## Install channels per host

A host can have several install channels that drift to different versions independently (observed on Codex three times: standalone dir vs Personal Plugin at different versions; the standalone dir vanished twice between turns). Rules: record the **Channel** in every install-log row; a version check reads the **installed files** (`SKILL.md` metadata, helper `--version`), never a catalog cache — if a cache is cited, record its mtime.

| Host | Channels |
|---|---|
| Claude | app skill upload (Settings → Capabilities → Skills); Claude Code plugin (`/plugin install` or marketplace) |
| Codex / ChatGPT | standalone skills dir (`~/.codex/skills/`); Personal Plugin (manual "Upload new version" in the ChatGPT UI — no API); desktop marketplace (cache `~/.codex/cache/remote_plugin_catalog/*.json` is stale evidence) |
| Antigravity | `~/.gemini/config/skills/` from the repository |
| Gemini Apps, Copilot, Grok, Opal | single upload channel each (UI) |

## Host notes

- **Claude app**: upload the Universal ZIP under Settings → Capabilities → Skills (repo-documented path).
- **Claude Code**: plugin ZIP or the repository marketplace (`.claude-plugin/marketplace.json`).
- **Codex / ChatGPT**: plugin via Plugin Creator; standalone skill via the Universal ZIP.
- **Microsoft Copilot**: the sandbox has no direct network; packaged scripts must not need it (repo).
- **Microsoft Copilot**: two distinct surfaces — **Agent Builder** (Configure → Skills, takes the agent ZIP) and **chat attachment** (observed 2026-10-07 rejecting the ZIP but accepting Markdown files attached directly). The documented Agent Builder layout is root-level `SKILL.md`, while this package nests it under `<name>/`; whether Agent Builder requires the root layout is an **open question** (pending test, ai-prompting W-015) — do not change the package until that test runs. The sandbox has no direct network; packaged scripts must not need it (repo).
- **Grok**: sandbox is POSIX; OneDrive/SharePoint hydration cannot be checked there (repo).
- **Gemini Apps**: never rename or obfuscate code to pass the scan; omit and document.
- **Opal**: references must be flat for the skill to work there; keep essential instructions in `SKILL.md` and top-level `references/`.
- **Antigravity**: installs from the GitHub repository (repo-documented; untested here).
- **Local models (Ollama, LM Studio, llama.cpp front-ends)**: point the system prompt or context loader at `SKILL.md`.
- **Manus**: not in v1; no package rules recorded.
- **Sandboxed review hosts (Opal, Manus, Copilot)**: review-only for releases — never do git work there. Observed: an Opal workspace reset wiped a clone before it could push (2026-10-07).

## Open questions

- Does Gemini accept `.yml` templates? (github-release omits them as a precaution.)
- Is the Opal size limit per file or per package, and what is the exact threshold?
- Which hosts load an `AGENTS.md` from a project folder? Not a packaging rule; tracked in the project.
- Does Copilot Agent Builder require root-level `SKILL.md` in the ZIP? (See Microsoft Copilot host note; pending W-015 test.)
4 changes: 4 additions & 0 deletions skills/github-release/references/publish.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Observed 2026-10-08 (multi-agent-folder-cleanup v1.6.2 pilot): a cloud agent ses

When a step is refused from the cloud, do not retry through another route; hand the owner the exact command and wait. The refusal changes where the step runs, never the approval for it.

Prefer `git push` from a real clone. If a connector/API push is the only route, it can **alter file bytes** (observed 2026-10-06: a connector decoded `\u` escapes in a pushed Python file into literal characters; caught only by hash comparison). After any non-git push, before opening the PR, compare every pushed blob with the local file: `gh api repos/<o>/<r>/contents/<path>?ref=<sha> --jq .sha` must equal `git hash-object <path>` locally.

- Typical flow: feature PR (CI green) → merge → release PR with version bump (CI green) → merge → annotated tag on the merge commit.

```bash
Expand Down Expand Up @@ -77,10 +79,12 @@ With immutable releases enabled in repository settings, assets and the tag are l

- Download the published assets again into the project's local release folder and run `verify_release.py --expect-published --expect-immutable` against the published metadata (`gh api repos/<owner>/<repo>/releases/tags/v1.2.0`).
- Published releases do not update any host. Each host install is a separate action and a separate install-log row ([verify-and-record.md](verify-and-record.md)).
- Delete the merged release branch (approval), recording its tip SHA for recovery; list any other remote branches whose tips are ancestors of `main` for a separate owner decision. Repositories with "automatically delete head branches" enabled (repo-setup default) skip the first step.
- Update the project's records: tracker, quick context, index.

## Never

- Renaming a branch that has an open PR — GitHub closes the PR and it cannot be reopened (observed 2026-10-08). A renumbered candidate gets a new branch and a replacement PR; close the old PR with a comment linking forward. `--force-with-lease` only on your own un-reviewed branch, never on `main`.
- `gh release upload --clobber` or deleting and re-uploading a published asset.
- `--generate-notes` as a substitute for the reviewed notes file.
- Publishing from a session that cannot show the verify result.
Loading
Loading