Agent skill
public-changelog
Create or append the workspace's public product changelog, a permanent public page that Octave's release-notes ingestion re-reads to propose Product and Core Feature updates.
From octavehq/lfgtm · 28 skill entries · 11 · pushed 2026-11-02
What it does when it runs
Create or append the workspace's public product changelog, a permanent public page that Octave's release-notes ingestion re-reads to propose Product and Core Feature updates. Takes the entries approved for a period (pasted from a changelog staging page, or described in plain words), checks them against publish rules, and adds them without changing any line already live. Use when the user says \"publish to the changelog\", \"add these to the public changelog\", \"start a public changelog\", or pastes approved changelog entries.
Automated analysis of the skill and the 2 files bundled beside it. A skill’s own description is written to be selected by an agent, so it describes the job and not the dependencies.
- Keys and connectors you must supply
- ARTIFACTS_ACCESS_TOKEN
- Hosts it reaches
- No third-party host appears in the skill or its bundled files.
- Tool permissions it declares
- No
allowed-toolsin the frontmatter. It does act, so it runs under whatever permissions your session already grants. - Actions present in the files
- shell
Ask about public-changelog
Opens your assistant with this page's recorded context. Check the linked sources before relying on it.
Install it
View source on GitHub ↗git clone --depth 1 --filter=blob:none --sparse https://github.com/octavehq/lfgtm.git /tmp/lfgtm git -C /tmp/lfgtm sparse-checkout set "skills/public-changelog" mkdir -p ~/.claude/skills/public-changelog cp -R "/tmp/lfgtm/skills/public-changelog/." ~/.claude/skills/public-changelog/
Picked up without a restart. A project skill of the same name is shadowed by your personal one. For one repository only, swap ~/.claude/skills for .claude/skills. Claude Code docs ↗
Or take the whole library
This repo ships a .claude-plugin manifest, so Claude Code can install all 28 skills at once. Plugin skills are invoked as /<plugin>:<skill>, so they never collide with your own.
/plugin marketplace add octavehq/lfgtm /plugin
The folder is the same in every client that implements the format — 46 of them — so if yours is not above, only the destination changes.
Before you install: this skill will not complete its job on a bare agent. It needs ARTIFACTS_ACCESS_TOKEN, which you have to obtain separately.
The skill
Source on GitHub ↗Reproduced in full from octavehq/lfgtm/blob/228b8983ef7bba9143c3de359bfd63f630dcb9b8/skills/public-changelog/SKILL.md, which is licensed MIT (repository). 2,034 words, 14 headings.
/octave:public-changelog - Publish the Public Changelog
The public changelog is one permanent public page per workspace. Octave re-reads it on a schedule, converts it to markdown and diffs it as a set of lines: every line it has not seen before is read as a newly shipped capability and becomes a library suggestion. Two rules follow, and everything in this skill exists to keep them:
- A live line is never re-worded. Changing an old entry's text makes it read as brand new and re-mints suggestions for something that shipped months ago. The only correction is a newer entry that supersedes it. The one exception is an entry still live as a paragraph: a reviewer on the staging page can reshape it into fields or remove it, and the paste names exactly those ids.
- Whatever goes on the page teaches the library, and so every rep's messaging. Only work every customer can use today belongs there: no fixes, infrastructure, betas or gated features.
So the page is rendered by a script from a state file, never written by hand. You supply a title and the entry's fields (a description, a use case, the problem it solves, and whether it is new or an improvement); changelog.py owns the markup, and refuses to produce a bundle unless every line already live comes out byte-identical. The live page is the lock. It stays readable to someone outside engineering: keep messages plain, never show a stack trace, and when something needs an engineer, say so and stop.
Usage
/octave:public-changelog # asks for the entries to publish
/octave:public-changelog <pasted entries> # the JSON a staging page copied, or plain text
What the page holds
The asset is a website asset named public-changelog-<company-slug> (lowercase), privacy
public, status published, entry point index.html. Its bundle is exactly two files:
| File | Holds |
|---|---|
index.html | The rendered page. Its lines are what the crawl diffs. |
changelog.json | The page title, dek and footer, and every published entry (id, date, title, novelty, description, useCase, problem, publishedAt; an entry published before fielded entries has body in place of the four, and a reshaped one adds reshapedAt). removed lists the id and removedAt of every entry taken off the page, with none of its text. Public text only. |
Entries published as one paragraph stay that way unless a reviewer decides otherwise. Every
new entry is fielded. A paragraph entry changes only when the staging paste lists it under
reshape (its paragraph is replaced by the fields given) or remove (it comes off the page).
The public-changelog- prefix is load-bearing. Only /sites/public-changelog-… is open to
the ingestion crawler, and the path is case-sensitive; a page named anything else publishes
fine and is never read.
Input
From a staging page (for example Octave's own /product-changelog routine), the paste is:
{ "schemaVersion": 3, "kind": "changelog-entries", "runId": "2026-09-22",
"entries": [ { "id": "2026-09-18-motion-playbooks", "date": "2026-09-18",
"title": "Motion Playbooks", "availability": "ga", "novelty": "net-new",
"description": "Motion Playbooks let a sequence agent…",
"useCase": "An SDR can now launch a five-step sequence…",
"problem": "Generic multi-step sequences drifted off-message…" } ] }
When the reviewer decided entries still live as a paragraph, the paste also carries:
"reshape": [ { "id": "2026-07-14-deal-plans", "novelty": "net-new",
"description": "…", "useCase": "An AE can now…", "problem": "…" } ],
"remove": [ "2026-06-02-call-notes" ]
A reshape keeps the entry's id, date and title, so its heading stays live; only the paragraph under it is replaced. A removal takes the entry's heading and paragraph off the page, plus the month heading when nothing else is left under it. All of it goes out in the same publish as the new entries. Before step 5, tell the user that Octave reads reshaped fields as new content once, so a batch of Core Feature update suggestions follows.
An older staging page copies schemaVersion: 2 entries with a single body. Those are refused
as new entries; ask for the fields (or re-copy from an updated staging page). One already live
with identical text is skipped, as always.
Keep every id as given. The source system matches ids when it reads the page back, to mark
its own records as published.
In plain words ("add these three for Sept 1–15"): turn each item into the same shape.
Draft each entry's fields with writing entries, set date to the
ship date (ask if you cannot tell), and set availability: "ga" only after the user confirms
every customer can use it today. Omit id; the script derives one. Show the user each title and
its fields and get their approval on the wording before rendering. Once live, it is final.
Run it
Work in a scratch directory. Resolve every path below from this installed skill directory.
1. Find the page
Call verify_connection and say which workspace you are publishing to. Then assets_list with
search: "public-changelog". A match is an asset whose identifier starts with public-changelog.
- Several matches: ask which one. Never guess. Declaring the wrong one splits the history.
- A match with
canManage: false: stop, name its owner, and say that person must transfer it or publish themselves. Every write would fail. - No match: this is a first publish. Go to step 2b.
2a. Existing page: load its state
Mint the access token now (see step 5 for how), then download the current bundle:
bash <plugin-root>/skills/asset-manager/scripts/download-artifact.sh --uuid <uuid> --out current
- If
current/changelog.jsonexists, that is the state. - If only
current/index.htmlexists (a page published before this skill kept state), rebuild it:python3 scripts/changelog.py bootstrap --html current/index.html --out current/changelog.json. It refuses a page it cannot reproduce exactly. If it does, stop: publishing on top of a page you cannot reproduce risks changing live lines.
2b. New page: fix its chrome once
Resolve the company with get_workspace_company. Propose a title, a one-sentence dek and a
footer line, for example "Acme Changelog" / "What we shipped, and what it means for teams using
Acme." / "Acme, <their positioning line>". Tell the user these three lines are permanent:
changing one later re-extracts it as a release entry. Then:
python3 scripts/changelog.py init --title '…' --dek '…' --footer '…' --out current/changelog.json
Style it with the workspace brand, following brand kit usage:
pass the kit's tokens.css as --tokens and its verified logo files as --logo-light /
--logo-dark in step 4. The kit's --brand-* tokens drive the layout (titles in
--brand-font-heading, text in --brand-font-body, dates and labels in --brand-font-mono when
the kit defines one, otherwise the system monospace); without a kit the page falls back to a
neutral palette. Never hotlink a font or image. Page safety refuses an external
fetch, because fetched content becomes part of what the crawl reads.
3. Write the entries file
Write the entries to new.json as { "entries": [ … ] }. For a staging paste, write it as
pasted. Apply any spoken amendments on top ("drop the second one"), then read back one line per
entry (date, title, first words) for confirmation.
4. Render and check
python3 scripts/changelog.py add --state current/changelog.json --entries new.json \
--html current/index.html --out bundle # omit --html on a first publish
It refuses the whole batch on any problem, never part of it. A partial publish would hide an attempt to touch a live entry. Read each refusal back in plain words. The common ones:
| Refusal | Meaning | Fix |
|---|---|---|
| not marked generally available | an entry is gated, beta or unchecked | drop it; it publishes once it is GA |
| is a single paragraph | a body was sent for a new entry | rewrite it as fields (writing entries) |
| has no description / useCase / problem / novelty | a field is missing | fill it in |
| line break, markdown character | a field is not one plain paragraph | join it into one paragraph; drop the leading character |
| already live with different text | an id that is live was re-worded | leave it; publish a new superseding entry |
| reshape or remove … is not live, already fielded, keeps the title | a reshape / remove id is not a live entry, or a reshape changed its date or title, or targets a fielded entry | re-copy from the staging page; never hand-edit those lists |
| was removed from the page | a new entry reuses the id of one removed earlier | give it a new id |
✗ history | a live line would change | stop, an engineer is needed; never edit the state to force it |
✗ delta under the floor | too little new text to trigger ingestion | add the missing fields, or hold until there is more |
✗ line-uniqueness | a line repeats an existing one | reword the new entry's title or the repeated field |
On an existing page the stylesheet is carried over from the live page. Pass --tokens only
when the user asks to restyle; styling is not crawled, so restyling is safe. A restyle also moves
an older page onto the current layout (dates in a left column, the two fields as
labeled rows); without one, the page keeps the look it was published with.
On success it prints the exact lines this publish adds, and, when the paste reshapes or removes entries, the live lines those retire. Show them to the user and get an explicit go before step 5. This is a permanent public write.
5. Upload
Mint a fresh token with asset_generate_access_token immediately before the script batch, per
asset script usage: token in
ARTIFACTS_ACCESS_TOKEN, host in ARTIFACTS_URL from the mint's apiBaseUrl, never on the
command line or in a file. Minting rotates the user's token, so mint after every other asset
tool call.
Existing page (the upload replaces the whole bundle, which is why both files go every time):
bash <plugin-root>/skills/asset-manager/scripts/update-artifact.sh --uuid <uuid> --src bundle \
--manifest bundle.manifest.json --expected-version <current version> --note 'Changelog: +N entries'
First publish:
bash <plugin-root>/skills/asset-manager/scripts/upload-artifact.sh --src bundle \
--manifest bundle.manifest.json --identifier public-changelog-<company-slug> \
--description '<Company> public product changelog' --type website --privacy public \
--status published --entry-point index.html
Exit 3 means the write may have happened. Reconcile by uuid and version before any retry;
never blind-retry a public write. If an existing page is unpublished or not public (some
earlier setups held the page back until its first real entry), set it to published + public
with asset_update now that real content is on it.
6. Confirm it is really public
Fetch the siteUrl with no cookie or token and check it against the state:
curl -sS -f '<siteUrl>' -o live.html && python3 scripts/changelog.py lines --html live.html --expect-state bundle/changelog.json
The crawler has no session, so a page that redirects to a login is one nothing downstream reads. If this fails, report it and escalate. Do not republish.
7. Report
What went live, the public siteUrl, and anything skipped as already live. On a first
publish, also say what only a person can do in the app:
- A workspace owner declares the URL as release notes on exactly one Product
(Product → release notes → Connect release notes). Use the
/sites/public-changelog-…URL, not a vanity/s/URL: vanity paths are closed to the crawler, so the declaration succeeds and nothing is ever read. One Product only, because the first declared offering becomes the parent of every Core Feature the page produces. - If suggestions never appear after a crawl, release-notes ingestion may not be enabled for the workspace. Ask Octave support.
Mention that Octave re-reads the page on its own schedule; no further step is needed.
Never
- Hand-edit
index.htmlor a live entry inchangelog.json, or delete a live entry other than through a reviewer'sremove. - Enable asset↔resource sync on this asset. It would register the page a second time, extracted without the release-notes handling.
- Publish a workspace-only log, a staging page, or anything with customer names, deal values or internal ticket ids to this asset.
Files bundled with it
These load only when the skill asks for them, so they cost nothing until it runs.
Other skills for the same job
Different authors, same problem. Matched on the words in the skill name, across every library in the catalogue except this one.
- public-relations by coreyhaines31 · 57,989
- auto-research-public by growthenginenowoslawski · 798
- deliverability-test-public by growthenginenowoslawski · 798
- smartlead-campaign-upload-public by growthenginenowoslawski · 798
- zapmail-domain-setup-public by growthenginenowoslawski · 798
- changelog by GTMify · 25
- gtm-changelog by adaptico · 21
- zapmail-domain-setup-public by timyakubson · 8
Need help setting it up?
This page tells you what public-changelog does and what it needs. Cheetah builds the agent setup it runs inside: data, CRM, sequencing and the guardrails.
Book a call →The directory stays free. There is nothing gated behind this.