cut-release
Skill1 starsChanged 3 months agoMITView on GitHub
Cut an agentic-foundry release as a guarded playbook (/foundry:cut-release). Encodes the hand-run cut procedure as a loop whose EXIT GATE is the existing acceptance verdict — verifies the ordered preconditions (plugin.json version, CHANGELOG section, clean tree, and the candidate tree's own test suite; note the preflight TOLERATES a marketplace.json catalogue version left at the preceding release but the acceptance gate HARD-STOPS on it, so the catalogue bumps in R and only source.sha defers to the re-pin commit R2), refuses to emit any publish plan until run_acceptance returns pass, and only afterward emits the gotcha-correct publish plan (re-pin marketplace source.sha to the release commit → annotated tag on the re-pin commit → machine-verify the tag → push, never force) WITHOUT pushing. Trigger when the operator is cutting/releasing a version — "cut a release", "release v0.6.1", "/foundry:cut-release", "ship the release".
- Commits and pushes
36 other projects use a version of this.
Adopted by
Most recently taken up by
- GLinnik21/plx-nativetheir own version, first seen Nov 8, 2026
- mediajunkie/piper-morgan-producttheir own version, first seen Nov 8, 2026
- delexw/motion-graphics-agenttheir own version, first seen Nov 8, 2026
- V-Songbird/foundrytheir own version, first seen Nov 4, 2026
- tansu-io/tansutheir own version, first seen Nov 1, 2026
- rpraharaj/ai-sdlctheir own version, first seen Nov 1, 2026
Used with
Projects with it also use quota-axi, iterate-pr and 6 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
layer5ioSkillquota-axi
Report local Claude, Codex, Cursor, GitHub Copilot, and Grok quota windows via the quota-axi CLI - remaining percentages, reset times, and provider status read from local auth sources, with no routing, recommendation, or provider mutation. Use before deciding whether it is safe to keep spending a provider's quota, when the user asks about usage, rate limits, or remaining quota, or when comparing local provider headroom.
Also in 4 projects that use this
SentrySkilliterate-pr
Iterate on a PR until actionable CI passes and high/medium review feedback is addressed. Use for PR CI failures, review feedback, or green-check loops; do not wait for human approval, draft status, or merge gates.
Also in 4 projects that use this
layer5ioSkillgh-axi
Operate GitHub through the gh-axi CLI - issues, pull requests, workflow runs, workflows, releases, repositories, labels, Projects (v2), Actions secrets and variables, search, and raw API access. Use whenever a task touches GitHub: listing or filing issues, reviewing or merging PRs, checking CI runs, triggering workflows, cutting releases, managing Projects boards, or managing Actions secrets/variables.
Also in 4 projects that use this
kunchenguidSkillchrome-devtools-axi
Control a Chrome browser session through the chrome-devtools-axi CLI - navigate, snapshot, click, fill forms, run JavaScript, inspect console and network, take screenshots, audit performance. Use whenever a task needs a real browser: opening or testing a web page, clicking through a flow, extracting page content, or debugging a website.
Also in 4 projects that use this
kunchenguidSkilllavish
Turn complex or visual agent responses into rich, reviewable HTML artifacts (HTML files) the user can annotate and send feedback on, using the lavish-axi CLI. Use when about to give a plan, comparison, diagram, table, code diff, report, or anything easier to grasp visually than as prose.
Also in 4 projects that use this
ThisIsSadeghiSkillverify-ui
Verify UI implementation against Stitch design via a token audit (HTML ↔ Code, with X-component default-render checks) and an X-components compliance check.
Also in 3 projects that use this
Other versions36 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 36
- ToolJet/ToolJet★ 41ksame name
- meshery/meshery★ 12ksame name
- danielmeppiel/apm★ 4.2ksame name
- microsoft/apm★ 4.2ksame name
- Screenly/Anthias★ 3.7ksame name
- Netflix/bpftop★ 2.7ksame name
- tansu-io/tansu★ 1.9ksame name
- fim-ai/fim-agent★ 1.6ksame name
- fim-ai/fim-one★ 1.6ksame name
- libredb/libredb-studio★ 1.3ksame name
26 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository71 other files it gives its agents.
- AGENTS.mdAGENTS.md
- llms.txtllms.txt
- amendSkill
- auditSkill
- authorize-releaseSkill
- authorizeSkill
- certify-localSkill
- certify-stagingSkill
- coherence-checkSkill
- command-deckSkill
- contextSkill
- dashboards-as-codeSkill
- data-tier-cutoverSkill
- decommission-gateSkill
- deploy-statusSkill
- dispatchSkill
- doctorSkill
- drift-sweepSkill
- env-hygieneSkill
- extract-specSkill
- fleetSkill
- grounding-conformanceSkill
- id-applySkill
- id-architectSkill
- id-baselineSkill
- id-discoverSkill
- id-documentSkill
- id-driftSkill
- id-impactSkill
- id-implementSkill
- id-importSkill
- id-planSkill
- id-promoteSkill
- id-reviewSkill
- id-rollbackSkill
- id-simulateSkill
- id-syncSkill
- id-testSkill
- id-validateSkill
- id-verifySkill
- indexSkill
- infra-sandboxed-applySkill
- initSkill
- intakeSkill
- learn-captureSkill
- learn-distillSkill
- merge-when-greenSkill
- mode-autonomousSkill
- mode-interactiveSkill
- modeSkill
- post-upgradeSkill
- releaseSkill
- relockSkill
- report-citation-graphSkill
- reposSkill
- research-captureSkill
- research-firstSkill
- revertSkill
- sd-debugSkill
- sd-discoverSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
8 rules. 0 are common, 1 is distinctive, and 7 are very specific to this project.
0
Common
Most of the 36 edited versions we read keep these.
1
Distinctive
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 189
Commit it path-scoped: git commit -m '…' -- .claude-plugin/marketplace.json.
- Very specific: line 236
Run it from inside the workspace directory.
- Very specific: line 257
Every claude invocation it makes is drawn from one frozen, closed six-subcommand allowlist.
- Very specific: line 309
Do this ONCE per affected scope.
- Very specific: line 312
Verify by READING the refreshed cache — ~/.claude/plugins/marketplaces/<marketplace>/.claude-plugin/marketplace.json must show the new …
- Distinctive: line 328
Do not bulk-push changes into external-org / dirty / feature-branch repos.
- Very specific: line 346
The OPEN-ER advisory: When gh is reachable, READY prints which traced ERs are still OPEN at cut time (the ones the reconciliation targets).
- Very specific: line 362
Every atom's own PR targets release/<version>, not main — /foundry:merge-when-green refuses the wrong base when --release <id> is passed …
“Common” is measured: kept by at least 6 in 10 of the 36 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
/foundry:cut-release — the cut-release playbook
The FIRST foundry playbook (a guarded, looping skill — task-triggered, loop held by an
EXIT GATE, floors orthogonal; see docs/TERMINOLOGY.md). It turns the release-cut procedure — which lived only as memory-prose
and was hand-run ~6× (every recurring gotcha was that procedure failing) — into a self-contained,
gated loop driven by scripts/foundry-cut-release.py.
Its EXIT GATE is the EXISTING acceptance verdict (foundry-release-acceptance.run_acceptance) — the
only gate this script invokes; it reimplements no gate logic. The closure derivation
(foundry_release.py) is upstream prep the operator runs before the cut, not invoked here. It
adds no new floor and never runs git tag / git push — it emits the publish plan as
data and the operator executes it.
Retired: the wiring-pin migrate. This loop once named a
foundry-wiring-hash.pywiring-pin migrate step, re-checked transitively via DOCTOR-GREEN. That mechanism —scripts/foundry-wiring-hash.py, the.foundry/wiring-hash.pinfile, and the doctor's wiring-hash check — was retired; the currentfoundry-doctor.py(a thin 5-check probe,skills/doctor/SKILL.md) carries no wiring-hash check to re-check. What carries the load today is thefoundry-release-acceptancegate (run_acceptance) this loop's EXIT GATE already calls. Seeskills/release/SKILL.md's "Gate-wiring pin & manifest" tombstone for the fuller history.
The playbook shape (the standard template — see docs/TERMINOLOGY.md §6)
- TRIGGER — the operator is cutting/shipping a release (description-matched; no global pointer).
- ENGINE —
deterministic-seam-drive: ordered prep-check → machine gate → emit plan. - EXIT GATE —
foundry-release-acceptance.py run_acceptance(tree) == pass(the artifact-sha-bound verdict). The loop is NOT "done" until the gate is green; nothing reaches tag/push before that. - FLOORS TOUCHED — none new. The orthogonal floors (front-authorization, the per-atom merge floor, the acceptance HARD-STOP) already fired upstream; cut-release only sequences and refuses-until-green. It never folds a floor into a step.
The loop
run foundry-cut-release.py --tree <repo> --version <X.Y.Z>
│
├─ state=REFUSED (preflight) → fix the NAMED precondition (in the frozen order), re-run
├─ state=GATED (acceptance) → fix the acceptance FAILURE (doctor RED / validate / hooks), re-run
└─ state=READY → the publish plan is emitted; the OPERATOR executes itThe READY plan's tail carries the ER-reconciliation backstop — see below.
Procedure
Pick the version (the operator picks it — there is no inference) and make sure the prep is staged: bump
plugin.jsonalone → bump the install pin's remaining binding (below) → stage BOTH npm packages (next paragraph) → write the## vX.Y.ZCHANGELOG section → commit (the release commit R).TWO npm packages ship on a
v*tag, not one.npm-publish.ymlruns two sequential jobs:publish-cli(cli/→ create-agentic-workspace, the scaffolder) and thenpublish-cli-update(cli-update/→ update-agentic-workspace, the upgrader), the secondneeds:the first and polls the registry for its dependency before installing. Staging a release means staging both, and they move together in R — onlysource.shadefers to R2:file field commit rule .claude-plugin/plugin.jsonversionR the plugin version itself .claude-plugin/marketplace.jsonversion,source.refR not R2 — see the trap below .claude-plugin/marketplace.jsonsource.shaR2 the ONLY field that defers; it names R, which does not exist yet docs/+cli/permission-floor.jsongenerated_for_plugin_versionR both, byte-identical README.mdthe **Status: vX.Y.Z.**lineR asserted equal to plugin.json's versioncli/package.jsonfoundry.plugin_versionR asserted EQUAL to marketplace.json'sversion, so it follows the cataloguecli/package.jsonversionR the scaffolder tarball; a row in TARBALL_VERSION_BY_PLUGIN_PIN, keyed byplugin_versioncli-update/package.jsondependencies.create-agentic-workspaceR exactly cli/package.json's newversioncli-update/package.jsonversionR must ALSO move; a row in CLI_UPDATE_VERSION_BY_PIN⚠ THE TRAP: the preflight and the acceptance gate disagree about the catalogue bump
Read this before staging anything, because two of this playbook's own gates give opposite answers and only one of them is a HARD-STOP.
- The preflight tolerates
marketplace.jsonsitting at the immediately-preceding release and says in as many words that "the bump may be deferred to the re-pin commit R2". - The acceptance gate —
claude plugin tag --dry-run, run insiderun_acceptance— then REFUSES exactly that state: "Version mismatch: plugin.json says X but .claude-plugin/marketplace.json plugins[0].version says Y … update the marketplace entry before tagging." That is a HARD-STOP; the cut cannot reachREADY.
So the tolerance is not usable.
marketplace.json'sversionandsource.refbump in R, with everything the equality chain drags along:cli/package.json'splugin_version(asserted equal to the catalogue version bytest_the_plugin_pin_block_matches_the_marketplace_manifest, no R-to-R2 tolerance), then itsversionviaTARBALL_VERSION_BY_PLUGIN_PIN, thencli-update's pin and version viaCLI_UPDATE_VERSION_BY_PIN.source.shais the only field that can defer to R2 — it names R, which does not exist until R is committed, and that is the whole reason R2 exists.The v1.10.0 cut lost two full preflight runs (~12 minutes each) rediscovering this: first by staging the CLI pins in R against the deferred catalogue (suite red), then by deferring the catalogue as this playbook said (acceptance HARD-STOP). Stage as the table says and neither State the trade honestly: deferring
source.shadoes NOT preserve AC-ILU-11's property — it is the field that breaks it. What it preserves is only the weaker, unavoidable "never pin a commit that does not exist yet", sincesource.shanames R. Between R and R2,main's catalogue advertises the new version while pinning the PREVIOUS release's tree, so an adopter resolving tagless in that window installs the previous code labelled as the new version — andshaoutranksrefin resolution. It is sticky: R2 moves onlysha, andmanifestRefreshedneeds BOTHversionandshato move, so their next refresh reportsalready current.main'sciis RED for the whole window —foundry_main_catalogue.pyruns on push-to-main and exists to convict exactly this state; the workflow scopes it to push-only because the window is legitimate. That red is the window reporting itself, not a defect to fix in the gate. So: land R and R2 back to back. Every minute of that window is an adopter-visible mislabelled install. The window is not new — v1.9.1 had it too — but nothing in this playbook said so before.Why
cli-update's own version must move whenever its pin does. The publish workflow skips a version already on the registry.update-agentic-workspace@<old>is published with the previous dependency baked in, so bumping only the pin leaves the registry serving an upgrader that installs the previous shared modules — a fully green release delivering the fix to nobody. On a release that first introduces a shared module it is worse: the published pin resolves to a version that does not contain it, andnpx update-agentic-workspace@latestinstalls and then crashes on import.Both tables live in
tests/test_bootstrap_cli.pyand both refuse an unrecorded pin, so the preflight suite names this rather than trusting you to remember it. Add each row deliberately, with the one-line reason — that is the point of a table over a diff.RETIRED — this paragraph used to say
marketplace.jsondoes not bump in R. It claimed theversionandsource.refbump moved to R2 alongsidesource.sha, on the AC-ILU-11 reasoning that the default branch must never advertise a version its own pinned commit does not carry. No cut has ever done that, because the acceptance gate refuses it (the trap above), and v1.9.1's own R bumpedversion+refwhile leavingshaat the previous release's commit — exactly what step 1's table now prescribes. The text is kept as a tombstone because it is quotable and was believed; do not restore it.test_manifests_agree(tests/test_docs_claims.py) tolerates the R-to-R2 lag and still refuses amarketplace.jsonthat gets AHEAD ofplugin.jsonor whosesource.refnames a version it does not itself carry.⚠️ The install pin's remaining binding is NOT covered by this playbook's preflight. The
REFUSEDchecks below coverplugin.jsonand the CHANGELOG section — not:DEFAULT_MARKETPLACE_REFinscripts/foundry-bootstrap.sh— the installer's own default pin, a separate code-level binding carried by the siblingfeat-foundry-installer-unpinning;test_shipped_pin_matches_marketplace_manifest_refintests/test_bootstrap_install_pin.py, which compares that constant againstmarketplace.json'ssource.ref— and since that ref now lands in R2 rather than R (above), this comparison is only expected to agree once R2 lands, not at R itself.
The documented marketplace-add install lines are NOT a per-release binding any more.
README.md,docs/QUICKSTART.md,docs/troubleshooting.md,docs/how-to/adopt-on-an-existing-codebase.md, and this file's own "Downstream" section carry no version literal at all — there is nothing on them to bump on a cut (feat-foundry-install-line-unpinning). An adopter upgrades in place withclaude plugin update foundry@agentic-foundry; see "Downstream" below for why, and for the one-time migration an existing tag-pinned adopter still needs.Those live in the pytest suite. Since
feat-foundry-release-suite-gate, the preflight RUNS that suite and REFUSES the cut on any failure — so a stale pin can no longer reach a tag, and this list is guidance for staging the bump, not a checklist you must remember. It is here because knowing the remaining binding makes the refusal instantly actionable, not because you are the control.This is the fix for the v0.27.0 cut, which shipped a stale pin: its four metadata preconditions all passed and
run_acceptance(validate +plugin tag --dry-run+ DOCTOR-GREEN) runs no tests, soREADYwas returned truthfully while the tree was failing its own suite. The first remediation was a warning in this very paragraph — an instruction a human had to follow. That was the wrong control, and it was replaced with an enforced one.- The preflight tolerates
- Run the gate:
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/foundry-cut-release.py" --tree <repo-root> --version X.Y.ZREFUSED(exit 2) — a precondition failed; the output names it (e.g. "plugin.json version != target" for a typo'd version, or amarketplace.jsonthat is neither at target nor at the one CHANGELOG-recorded preceding release, per step 1's deferred-bump note above). Fix and re-run. This now includes the candidate tree's own test suite, which runs after the four cheap metadata checks (so a typo'd version still refuses in milliseconds) and before acceptance. A failure names the failing tests; anything that prevents a real verdict — pytest absent, notests/, nothing collected — refuses fail-closed rather than skipping. Expect the metadata-clean path to take a few minutes: that cost is bought once per release, and the alternative is a permanently wrong published tag.GATED(exit 2) —run_acceptancereturned FAIL; the candidate tree's own doctor / validate / hooks-executable check is red. Fix the defect (NOT the gate) and re-run.READY(exit 0) — preconditions ok ∧ acceptancepass. The publish plan is printed.
Execute the emitted publish plan yourself (cut-release never pushes). RE-PIN FIRST, TAG SECOND — this order is the whole point, and the previous order shipped the wrong code twice:
git status --porcelain— must be empty. The re-pin commit is created after the acceptance verdict and the tag lands on it, so any uncommitted edit would ship under the release tag having never been gated.CONTENT=$(git rev-parse HEAD)— the release commit R, the code being shipped.- Edit
.claude-plugin/marketplace.json:source.sha = $CONTENT— that field alone.versionandsource.refalready moved in R (step 1's table and its trap: the acceptance gate'splugin tag --dry-runHARD-STOPS on a catalogue version that disagrees withplugin.json, so deferring them cannot reachREADY). Deferringsource.shais what preserves AC-ILU-11's property: the pinned commit is R, which did not exist until R was committed. - Commit it path-scoped:
git commit -m '…' -- .claude-plugin/marketplace.json. Nevercommit -am, which sweeps every modified tracked file into the tagged commit. → R2 git tag -a vX.Y.Z— on R2, the commit that CARRIES the pin.python3 scripts/foundry-cut-release.py --tree <repo> --version X.Y.Z --verify-tag— the machine re-check. Must printTAG-PIN-COHERENT. Do not push until it does.push origin main+push origin vX.Y.Z, never force-push (if a parallel push rejects, reconcile by MERGE).On a protected
main,push origin mainis REJECTED — even for the operator. This repo'smainis Tier A (required contexts +enforce_admins), so R2 cannot be pushed directly; the tag pushes fine. Observed on v1.4.0: the tag landed and verified whilemainstayed a commit behind, leavingmain'smarketplace.jsonnaming the PREVIOUS release's sha. The published artifact is unaffected (adopters resolve at the tag), but the unstable--ref maininstall path subsequently serves the previous version and the next cut starts from a wrong baseline. So create R and R2 on a BRANCH and PR them from the start — two PRs, the bump followed by the re-pin, becausesource.shamust name the commit AS IT LANDS onmainand a squash merge does not preserve a branch commit's SHA. Read main's HEAD after the bump PR merges; that is the sha the re-pin PR pins. Tag only once R2 is onmain.
Why the order matters. An adopter installs by ref:
marketplace add <repo>#vX.Y.Zresolvesmarketplace.jsonat the tag and installs the commit itssource.shanames. Tagging before the re-pin leaves the tag serving the previous release's sha, so the install delivers the previous version's code — for a security patch, the fix is simply not delivered. That shipped on both v1.0.0 and v1.0.1 and was hand-corrected each time.Why step 6 is not optional. On the cut that creates the tag, the preflight coherence check reports "not applicable" — the tag does not exist yet, so there is nothing to inspect. Step 6 is the only point at which the property is machine-verified rather than assumed.
- Reconcile the release's ERs (the backstop's operator step). The
READYplan's tail carries onegh issue close <n>step per enhancement-request the release closes, derived from the## vX.Y.ZCHANGELOG section (the trace source authored in step 1). Review each before running it — see the backstop section below — and afterward execute the ones that apply. Closing an already-closed ER is a harmless no-op. Downstream — the documented install line registers the marketplace tagless (
feat-foundry-install-line-unpinning), so the complete adopter upgrade, forever, is ONE command:claude plugin update foundry@agentic-foundryOr, since v1.9.0, the shipped tool that does this and the checking around it:
npx update-agentic-workspace@latestRun it from inside the workspace directory. Prefer it over the bare
plugin updatewhenever the adopter's state is not known-good — it is the manual steps below made mechanical, and it is the only path that verifies delivery rather than reporting it:- Phase 1 — refreshes the marketplace, first migrating a registration still carrying a pinned
ref(the pre-v1.7.0 leftover): removed, re-added tagless, plugin re-installed, once per affected scope, that scope's other settings preserved. This is the one-time migration written out by hand further down; the tool finds the affected scopes instead of the operator guessing. It reads the marketplace catalogue manifest before and after, and reportschangedonly when BOTHversionandsource.shamoved — so an R2 that moves onlysource.shareportsalready currentdespite real delivery. - Phase 2 —
plugin updatein every scope whose settings enable it, from the pre-migration snapshot. ⚠ Phase 2 verifies nothing per scope.runPluginUpdate's result is discarded and Phase 2's printed verdict is a copy of Phase 1's catalogue delta, so a scope whose update silently no-ops still printschanged. The read-back above is a catalogue check, not a delivery check — you still confirm both scopes by hand (below). - Phase 3 —
--cleanup, opt-in: prunes superseded plugin-cache versions and a stale or duplicate registration no scope still enables. A flagless run previews and removes nothing. - Phase 4 — re-runs the managed-file and permission-floor reconcile, so an adopter's floor picks up rules a release added rather than staying at whatever the last scaffold wrote.
Every
claudeinvocation it makes is drawn from one frozen, closed six-subcommand allowlist; none can start a session or reach the trust dialog. One side effect to state when handing it over: healing a pinned-refscope ends inplugin install, so a scope where the plugin was deliberately disabled has it re-enabled. That is specified, not accidental — a stale pin is a broken registration either way — but an adopter who wants it off must disable it again afterward.That is the whole upgrade — nothing else to run.
plugin updateresolves the marketplace at the ref the registration names. A tagless registration names none, so resolution reads the catalogue live off the default branch every time; a new release reaches every adopter the moment R2 lands there, with no re-add and no per-release step on the adopter's side. The plugin id must be marketplace-qualified: a bareclaude plugin update foundryfails withPlugin "foundry" not found.Landing R2 on the default branch is delivery-critical, precisely because of that. A tagless registration resolves the catalogue from the default branch itself, never from a tag — so until R2 (the commit carrying the new
version/source.ref/source.sha, step 3) actually lands there,plugin updatekeeps reporting "already at the latest version", naming the PREVIOUS release, truthfully. Publishing a tag is not delivery; landing R2 on the default branch is. There is no install line to bump on this side and no per-release step to forget: the whole upgrade path is this one command, every time.One-time migration for an adopter still tag-pinned from BEFORE this change.
npx update-agentic-workspace(above) performs exactly this as its Phase 1, in every affected scope, and that is the way to do it. What follows is the same procedure by hand — read it to understand why each step is there, or run it when npm is not available:Their registration is
<owner>/<repo>#<old-tag>, andmarketplace updatealone is a no-op against it — it refreshes the cache at the ref already declared, which is the frozen old tag, forever. Re-runningmarketplace add <repo>tagless does not fix it either: it REFUSES outright, because a different ref is already declared for that marketplace insettings.json(the CLI's own message: "its network source differs from the one declared for it in settings"). The actual fix ismarketplace remove→ taglessmarketplace add→plugin install, notplugin update—marketplace removealso drops the plugin(s) installed from that marketplace (same as the "Wedged or stale install" recovery indocs/troubleshooting.md), so aplugin updateafterward would have nothing to update;plugin installputs it back. Run it explicitly per scope, in every scope that still carries the old tag-pinned registration — a bare invocation touches only the default scope, and a stale scope silently shadows a fresh one (see the--scope-mismatch caveat below):# user scope claude plugin marketplace remove agentic-foundry --scope user claude plugin marketplace add lukasrepublic/agentic-foundry --scope user claude plugin install foundry@agentic-foundry --scope user # project scope (repeat if a project-scoped registration was also tag-pinned) claude plugin marketplace remove agentic-foundry --scope project claude plugin marketplace add lukasrepublic/agentic-foundry --scope project claude plugin install foundry@agentic-foundry --scope projectDo this ONCE per affected scope. Every cut after that resolves automatically via the one-line
plugin updateupgrade above.Verify by READING the refreshed cache —
~/.claude/plugins/marketplaces/<marketplace>/.claude-plugin/marketplace.jsonmust show the newversionAND the newsource.sha; the CLI's own "success" line does not prove the ref moved.npx update-agentic-workspace@latestdoes NOT do this for you. Its read-back covers the marketplace catalogue only, in Phase 1; nothing reads back per-scope install state, so the both-scopes check in this paragraph is still yours to run. This is the v1.4.1 scar, and it is exactly the shape that produced it: a stale project row shadows a fresh user one, the tool printschangedfrom the catalogue delta, and the session keeps loading the previous version. Check both scopes: a stale project row shadows a fresh user one, so a user-scope update can report success while the session keeps loading the old tree. Identify the tree actually loaded by a version-unique path, never by process start time. Missed on the v1.4.1 cut, where the plugin sat at 1.4.0 through several "successful" update runs. For this repo's own adopters that isclaude plugin marketplace update agentic-foundry→claude plugin update foundry@agentic-foundry(claude plugin listprints the qualified id if you are unsure). Afterward, verify the SHIPPED artifact, not the source tree — run/foundry:doctorand any atom's--selftestagainst~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/. A green source tree does not prove the published one is green. (When invoking a cached script by hand, setCLAUDE_PROJECT_DIR— without it,stack-profile-lockreports a misleading "not applicable".) Do not bulk-push changes into external-org / dirty / feature-branch repos.- Phase 1 — refreshes the marketplace, first migrating a registration still carrying a pinned
The ER-reconciliation backstop
Enhancement-request issues should close when their implementing atom merges (the PR-level Closes #<n>
control). That control depends on the GitHub-Projects project-map.json cache, which is
absent when projection is disabled — so ERs silently slip and the backlog fills with shipped-yet-open
issues (this is why past releases have shipped enhancement requests that stayed OPEN in the tracker).
Cut-release is the durable backstop: on every cut it re-derives the release's ERs from the CHANGELOG
and emits their closes.
- Trace source = the
## vX.Y.ZCHANGELOG section, already authored on every cut. The tool matches only the explicitER #<digits>marker — never a bare#NNN— because the CHANGELOG cites the ER (e.g.ER #<n>) and its implementing PR (e.g.(#<n>)) in the same#NNNnamespace; a bare match would try togh issue closea pull request. An unmarked number is not an ER. - Emitted, never executed. Like the tag/push plan, the
gh issue closesteps are DATA the operator runs — cut-release never touchesgh. They are appended after the push steps so an ER only closes once its release is onmain. - The OPEN-ER advisory. When
ghis reachable,READYprints which traced ERs are still OPEN at cut time (the ones the reconciliation targets). Offline, the check is skipped and the plan steps are still emitted (fail-safe: an unknown ER is never reported closed). - REVIEW before running (the load-bearing caveat). The prose trace is deliberately broad and cannot
distinguish "closes ER #N" from a mere reference — e.g. a "split to ER #N" / "deferred to ER #N"
follow-up mention traces
#Ntoo (a CHANGELOG section can reference a deferred ER this way). Drop any such referenced-but-not-closed ER from the plan before executing; close only the ones the release actually shipped. The advisory's OPEN list is your review surface, not an auto-close order.
Release-branch flow (branch-and-worktree-discipline, AC-BWD-4, v1.16.0)
context/branch-discipline.md is the full discipline (cited, never copied); this is where it
lands in the cut-release playbook specifically — for the plugin's own repo AND for an adopter's.
skills/intake/SKILL.mdcreatesrelease/<version>frommainas its LAST step and records it in the release manifest asintegration_branch(scripts/foundry_release.py's optional field).- Every atom's own PR targets
release/<version>, notmain—/foundry:merge-when-greenrefuses the wrong base when--release <id>is passed (skills/merge-when-green/SKILL.md). - The R / R2 cut-release commits described above land ON
release/<version>, exactly as before — this playbook's own procedure is unchanged; only the branch they land on moves. - The ONE PR to
mainisrelease/<version>→main, opened once every atom in the release has landed on the release branch and this playbook'sREADYplan is in hand. It is what makes a per-merge deploy trigger fire once per release instead of once per atom (the charter's own observed failure). - After the tag exists on the resulting
maincommit, the release branch itself is deleted — the same "delete afterorigincontains the merge, never before" rule as any atom branch. Runpython3 "${CLAUDE_PLUGIN_ROOT}/scripts/foundry-worktree-gc.py" --dry-run --repo <dir>(add--include '<prefix>/*'for any branch prefix outside the built-in set; checkfiltered_out_refs; after--apply, readstatus—partiallists every deletion that did not happen infailed, while the exit code stays 0) first (mode flag BEFORE--repo— the permission-floor row is an argv prefix rule), then--apply --repo <dir>to sweep every already-merged atom/release branch and its worktree in one pass (seecontext/branch-discipline.md's own recipe). - A hotfix never uses a release branch at all:
hotfix/<id>→maindirectly, stated in the hotfix PR's own body.
What it does NOT do
- It never tags, pushes, or closes issues. It emits the plan — including the ER-reconciliation
gh issue closesteps — as data; the operator runs it. (The selftest proves zero side effects over a temp tree; the ER-state check is read-only and fail-safe.) - It reimplements no gate. The exit gate IS
foundry-release-acceptance.py; a non-passverdict is a HARD-STOP, not something cut-release re-derives or can override. - No version inference, no signing. The operator picks the version; release-time signing/provenance (Sigstore/SLSA) is a typed, dormant seam gated to the first public release.
The install pin: what source.sha is, and why it names the tag's PARENT
Decided 2026-08-07 (operator). This was an open design question — drop source.sha and let
ref plus tag protection carry the integrity claim, or keep it and document what it names. It is
kept, and the reasoning is recorded here so the next reader does not re-open it, or worse,
"fix" the parent relationship into a loop.
sha is not decorative — it OUTRANKS ref. Per the Claude Code plugin-marketplace docs
(verified 2026-08-07), a github/git/url plugin source takes ref ("Optional. Git branch or tag")
and sha ("Optional. Full 40-character git commit SHA to pin to an exact version"), and when both
are present the pinned commit is what gets installed. Since Claude Code v2.1.141 a deleted
upstream ref does not even block the install, as long as the pinned commit is still reachable. So:
| Resolved by | What an adopter gets |
| the marketplace catalogue | read at the ref — marketplace sources support ref only |
| the plugin itself | installed at sha — ref is the label and the fallback |
Therefore source.sha at tag vX.Y.Z names the tag commit's FIRST PARENT, and that is
structurally necessary rather than a defect. A commit cannot contain its own hash, so the pin
names the content commit R while the tag sits on the re-pin commit R2. tag_pin_coherence
enforces the adjacency (sha ∈ {tag commit, first parent}), so a pin reaching further back than
the parent is refused — that is the check, not an accident of it.
Why it is kept rather than dropped. Dropping it would delete R2, and with it the plan's ordering
hazard, the coherence gate and the adjacency invariant — a real simplification, honestly weighed.
Two things decided against it. First, this repo's own standing-versions rule binds it: "CI actions
pinned to a 40-char commit SHA, not @vN". Publishing its own supply-chain artifact pinned to a
mutable tag while requiring SHA pins of everyone else is incoherent, and the README already
advertises the project as SHA-pinned. Second, this plugin ships hooks that execute in an adopter's
session; tag protection is a policy control an admin can switch off, whereas a content-addressed
pin cannot be. The industry pattern points the same way — Homebrew pins a tarball checksum, Go
records dependency hashes, GitHub Actions consumers pin @<sha> — with the caveat that in all of
those the pin lives in the consumer or a separate index, never self-referentially inside the
artifact. Claude Code's same-repo catalogue is what forces the self-reference here. Splitting the
catalogue into its own repo would dissolve it structurally and remains the only clean alternative,
at a cost far above this decision.
KNOWN AND HARMLESS: the installed tree carries a stale
source.sha. Becauseshaoutranksref, what an adopter installs is R — and R's own.claude-plugin/marketplace.jsonstill carries the previous release's sha, since the correcting value only lands in R2. This is inert: the catalogue an adopter resolves is read at the ref (R2), never from the installed tree, so the stale field is never consulted by anything. Do not "fix" it. Writing R's own sha into R changes R's hash — the self-reference again — and chasing it produces an infinite re-pin loop. It is recorded here because this repo has shipped four separate stale-pin defects, and the next reader will otherwise find this one and treat it as a fifth.
Anti-patterns
- Editing the gate to make a red cut go green — fix the candidate tree, never the acceptance gate.
- Tagging before re-pinning. The tag must sit on the commit that carries the pin, or an install by ref serves the previous release. This is the defect that shipped twice; the plan's order is the fix and it is not a stylistic preference.
- Using
git commit -amfor the re-pin. The tag lands on that commit after the gate ran — path-scope it to.claude-plugin/marketplace.jsonso nothing ungated rides along. - Pushing without running
--verify-tag. The preflight check is a no-op on the cut that creates the tag; skipping the post-tag re-check leaves the property entirely unverified. - Pinning
source.shato an annotated-tag object, a branch name, or an abbreviated hash — it must be a full 40-character commit id. A ref name is a mutable pin: what an adopter resolves changes as the ref moves. And sinceshaoutranksrefat install time, a bad value here is not softened by a correct tag — it IS what ships. - "Correcting"
source.shaat the tag so it names the tag commit. It cannot: a commit cannot contain its own hash. The parent relationship is the design (see The install pin above), and every attempt to close it produces a new commit that needs a new pin, forever. - Force-pushing to reconcile a parallel push — reconcile by merge; the tag is already immutable.
- On a harness denial of a cut-release command — distinct from this loop's own
REFUSED/GATEDstates — seedocs/harness-denial-fallback.mdand STOP: hand back the exact denied invocation; never retry it or route around it.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

