ci(release): publish curated release notes, never an empty body (#48)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Alice <alice@prismshadow.com>
This commit is contained in:
@@ -159,11 +159,37 @@ jobs:
|
||||
sha256sum "$f" > "$f.sha256"
|
||||
done
|
||||
|
||||
# Release notes come from changelog/<version>/RELEASE.md, written during release preparation and
|
||||
# committed BEFORE the tag (the release job runs on the tag's checkout, so a file added afterwards
|
||||
# is invisible here). Present and non-empty -> published verbatim as the body; absent -> GitHub
|
||||
# generates a body from the merged PRs. Either way the Release can never ship an empty body, which
|
||||
# is what happened to v0.1.1: the step passed neither body nor generate_release_notes, the action
|
||||
# left the body unset, and the omission was only visible once the Release was public.
|
||||
- name: Resolve release notes
|
||||
id: notes
|
||||
env:
|
||||
TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}
|
||||
run: |
|
||||
NOTES="changelog/${TAG#v}/RELEASE.md"
|
||||
if [ -s "$NOTES" ]; then
|
||||
echo "Publishing curated release notes from $NOTES"
|
||||
echo "body_path=$NOTES" >> "$GITHUB_OUTPUT"
|
||||
echo "generate=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "No $NOTES; falling back to GitHub-generated notes."
|
||||
echo "body_path=" >> "$GITHUB_OUTPUT"
|
||||
echo "generate=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# On tag-push use the ref name; on manual dispatch use the input tag.
|
||||
# An empty body_path is falsy for the action and simply ignored (it never tries to read it), so the
|
||||
# fallback branch cleanly leaves generate_release_notes to compose the body on its own.
|
||||
- name: Publish GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}
|
||||
body_path: ${{ steps.notes.outputs.body_path }}
|
||||
generate_release_notes: ${{ steps.notes.outputs.generate }}
|
||||
files: |
|
||||
dist-artifacts/*.tar.gz
|
||||
dist-artifacts/*.sha256
|
||||
|
||||
@@ -86,6 +86,11 @@ pnpm test:e2e # core live-model e2e, need
|
||||
`changelog/<version>/README.md`. The layout is documented in
|
||||
[`changelog/README.md`](changelog/README.md). Related changes may share one entry
|
||||
file (extending its details) instead of opening a new file per small change.
|
||||
- **A release ships its own announcement**: `changelog/<version>/RELEASE.md` is published
|
||||
verbatim as the GitHub Release body. Write it during release preparation and **commit it
|
||||
before creating the tag** — the release workflow reads it from the tag's checkout, so a
|
||||
file added later never reaches the Release page. Without it the workflow falls back to
|
||||
GitHub's auto-generated notes.
|
||||
- README assets under `assets/readme/` are generated — the benchmark charts from the
|
||||
landing benchmark data, and the demo screenshots via
|
||||
`node packages/landing/scripts/capture-readme-demo.mjs` (build first; needs Playwright
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
PenguinHarness 0.1.1 — Gemini 3.6 support, a self-upgrading CLI, and the skills that let an agent serve and fine-tune its own models.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
curl -fsSL https://penguin.ooo/install.sh | sh
|
||||
penguin web
|
||||
```
|
||||
|
||||
Linux and macOS, x64 and arm64, with a bundled Node runtime. Or via npm (needs Node >= 24):
|
||||
|
||||
```sh
|
||||
npm install -g @prismshadow/penguin-cli
|
||||
```
|
||||
|
||||
## Highlights
|
||||
|
||||
**Gemini 3.6.** `gemini-3.6-flash` and `gemini-3.5-flash-lite` are in the model catalog, on both the Google endpoint and OpenRouter, with the full 1,048,576-token context and vision. The rest of the catalog was rebuilt against AgentHub 0.4.1's supported-model registry and grew from 59 to 70 entries — Claude 5 on Anthropic, Kimi K3 on Moonshot, and more on OpenRouter and SiliconFlow.
|
||||
|
||||
**Serve and tune your own models, by asking.** Three new skills — vLLM, Ollama and LlamaFactory — teach an agent to stand up and fine-tune the models it runs on: describe what you want in plain language and it serves the model, scores itself, trains on what it got wrong, redeploys and measures again. With a local engine the data never leaves the machine.
|
||||
|
||||
**`penguin update`.** Upgrades an existing install through the mechanism it was installed with — the tarball installer or a global npm/pnpm/yarn/bun package — after showing exactly what it will do. `--check` reports without changing anything, and your data root is never touched.
|
||||
|
||||
**A sidebar that scales.** Conversations group by Workspace (or by Agent), groups can be pinned, sessions created by subagents and scheduled tasks file into their own folders, and long lists load a page at a time. Collapsed, the sidebar is now an eight-entry navigation rail with bilingual tooltips.
|
||||
|
||||
## Notable in this release
|
||||
|
||||
- **Subagents follow the parent session.** A spawned subagent inherits the parent's model and thinking level instead of falling back to the Project default, and a resumed session restores the level its Trace recorded.
|
||||
- **Empty tool lists stay off the wire.** Strict OpenAI-compatible servers such as vLLM reject `tools: []`; tool-less requests (the connectivity probe, title generation, the vision describer) now omit the field entirely.
|
||||
- **Two new prompt guardrails.** The agent no longer kills processes on the harness's own service ports — it picks a free port instead — and on an API key error it retries once, then stops and asks you to update the key outside the chat.
|
||||
- **Per-model max output tokens.** A 32k-context model no longer 400s because the agent-level output cap does not fit its window.
|
||||
- **Thinking level moved into the conversation**, next to the model picker, and writes through to the Agent's settings.
|
||||
|
||||
## Requirements
|
||||
|
||||
Linux or macOS (x64 / arm64). The installer bundles its own Node runtime; installing from npm needs Node >= 24. All data stays under `~/.penguin/data`.
|
||||
|
||||
Full detail: [changelog/0.1.1/](https://github.com/Prism-Shadow/penguin-harness/tree/main/changelog/0.1.1)
|
||||
+3
-1
@@ -4,7 +4,9 @@ The root [../CHANGELOG.md](../CHANGELOG.md) keeps one brief line per release. Wo
|
||||
|
||||
- `<folder>/README.md` — the summary: one brief line per change, each linking its detail file, e.g. `- [YYYY-MM-DD] Brief description. ([details](YYYY-MM-DD-short-slug.md))`
|
||||
- `<folder>/YYYY-MM-DD-short-slug.md` — one detail file per entry, named by the entry date: an H1 title, then what changed and why, using `##` sections once the entry covers more than one thing.
|
||||
- `<version>/RELEASE.md` — the announcement published verbatim as the GitHub Release body: a lead sentence, then `## Install`, `## Highlights`, `## Notable in this release`, `## Requirements`, and a closing link to this folder. Only a numbered folder has one; it is written at release, when the version is finally known.
|
||||
- Entries are grouped by the surface they change — Models, Web App, landing site, skills, docs, tooling — rather than one file per commit. A related change extends the existing file and its summary line instead of opening a new one.
|
||||
- Unreleased changes go in `unreleased/`, never in a numbered folder: the next version number is not knowable while the work is being written — the batch that accumulated as `0.2.0/` shipped as 0.1.1 — so a guessed number only creates a rename to get wrong later. At release, rename `unreleased/` to the version actually shipped, swap its `# Unreleased` heading for `# Version X.Y.Z` plus a `Released on <date>.` line, add the release line to the root file, and create a fresh empty `unreleased/`. Released folders are frozen.
|
||||
- Unreleased changes go in `unreleased/`, never in a numbered folder: the next version number is not knowable while the work is being written — the batch that accumulated as `0.2.0/` shipped as 0.1.1 — so a guessed number only creates a rename to get wrong later. At release, rename `unreleased/` to the version actually shipped, swap its `# Unreleased` heading for `# Version X.Y.Z` plus a `Released on <date>.` line, write `RELEASE.md`, add the release line to the root file, and create a fresh empty `unreleased/`. Released folders are frozen.
|
||||
- `RELEASE.md` must be **committed before the tag is created**: the release workflow reads it from the tag's own checkout, so a file added afterwards is never published. When it is missing, the workflow falls back to notes GitHub generates from the merged PRs rather than publishing an empty body.
|
||||
|
||||
Written in English. History starts after the v0.0.1 release (2026-07-19); earlier changes are not backfilled.
|
||||
|
||||
Reference in New Issue
Block a user