agentleFS
a-novel-kita-novel-kit/stack/.agents/skills/manage-versions/SKILL.md

manage-versions

Skill2 starsChanged 5 days agoMITView on GitHub

Keep cross-repo changes version-compatible through development, releases, and consumer pins. Load when repos must remain compatible to merge: unreleased dependencies, proto/REST contract changes, or breaking published symbols.

  • Installs packages
  • Commits and pushes

The file

We split this skill into the rules it gives an agent

12 rules. About 0 are common, 5 are distinctive, and 7 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈5

    Distinctive · estimate

    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
  • could be automatic: a setting could enforce it
  1. Very specific:

    Every repo in a-novel and a-novel-kit is independently versioned, and the repos depend on each other (services pin golib, jwt, each other's …

    line 11
  2. Very specific:

    Renovate keeps consumers current: After a dependency releases, Renovate opens a PR in each consumer bumping the pin and running the …

    line 35
  3. Distinctive:

    Every SHA must be on the default branch, or the run fails before creating anything.

    line 146
  4. Distinctive:

    Security hotfix (embargoed): When the fix can't be public before release:

    line 164
  5. Distinctive:

    Fix a red bump PR on its own branch: When the release needs a code migration, such as a visual change a consumer's tests pin, push only …

    line 234
  6. Very specific:

    Add the new versions next to the old ones, then run pnpm install.

    line 251
  7. Very specific:

    Remove the old entries, then pass pnpm install --frozen-lockfile.

    line 252
  8. Very specific:

    Never hand-merge pnpm-lock.yaml during a rebase.

    line 254
  9. Distinctive:

    If B's change is itself a dependency of a repo C, repeat: A → release → Renovate bumps B → B releases → Renovate bumps C → C merges.

    line 259
  10. Very specific:

    Search both app/ (services) and kit/ (other shared libraries) — a graduated-package change rips through jwt consumers as easily as service …

    line 329
  11. Very specific:

    Use the BREAKING CHANGE: commit footer (git-conventions) only on step 3 — the actual removal — not on step 1.

    line 369
  12. Distinctive:

    When a change spans repos:

    line 376

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Cross-repo version management

Every repo in a-novel and a-novel-kit is independently versioned, and the repos depend on each other (services pin golib, jwt, each other's pkg/go; deployments pin each other's Docker images; clients pin published packages). When a change touches more than one repo, the versions must line up before anything merges to production. This skill governs that.

Load it with whichever repo-kind skill applies (write-go-kit for a kit/ change, write-go-service for a service change), with git-conventions (the breaking-change commit footer) and implement-feature (the layered-branch workflow these cross-repo plans extend).


How things are versioned

  • Each repo is git-tag semver. A release is a vX.Y.Z git tag pushed on a master commit; the next version comes from the commit history since the last tag (fix:/chore: → patch, feat: or an absorbable breaking change → minor). A ! does not force a major here: a major is a planned vX-line initiative, never derived from a commit range — prepare-release owns that policy and the sizing. The tag push triggers the release workflow (a-novel-kit/workflows/publish-actions/auto-release), which cuts the GitHub Release and, for service repos, builds and publishes the Docker images / npm packages tagged with that version.
  • go.mod pins exact released tags. github.com/a-novel-kit/golib v0.20.31, not a range, not a branch. No replace directives anywhere in this codebase — the dependency graph stays reproducible from go.mod alone.
  • Renovate keeps consumers current. After a dependency releases, Renovate opens a PR in each consumer bumping the pin and running the configured post-upgrade tasks (go mod tidy, regenerate proto, …). A released version reaches a consumer's master only through Renovate, a feature branch waiting on it included, as bring a release through Renovate shows. Only a draft's pseudo-version pin, below, is ever written by hand. Production is the exception: a service or platform release pins its own images in infra, through infra's pin-release.yaml.
  • Docker-image pins in CI / compose files follow the same rule: pin a concrete vX.Y.Z (or ${{ github.ref_name }} for the repo's own image), never latest or a branch.

Cutting a release

A release is cut in CI, not from a developer's working tree. An admin triggers the repo's release workflow and picks a release type (patch / minor / major), or the release train does (coordinate-landing). release.yaml then runs in three parts (a-novel/.github#414):

  • plan runs the release-core action as a dry run, with no environment and no credential: it computes the version and checks the bump, the doc stamp and the tags. A rehearsal runs only this.
  • cut runs release-core in the protected release environment as the release App, whose key only that environment holds. It bumps version files (workspace-aware), refreshes doc refs (prepublish:doc / a-novel publish stamp), commits, tags vX.Y.Z, pushes the commit and tags together, and creates the GitHub Release.
  • The tag publishes. The root tag runs release.yaml again, and that run builds and attests the images, attests and publishes the npm tarballs, and dispatches infra's pin-release.yaml.

A repository that has not adopted the split yet releases in one job as the [Agent] bot; the workflows repo's docs/migrations/v1.57.0.md is the adoption guide.

There is no local release command, and no per-repo publish.sh or publish:* pnpm script — do not write or revive any of them. The bump is workspace-aware: release-core detects whether the repo is a pnpm workspace (a pnpm-workspace.yaml is present) and runs the matching pnpm version form — --recursive for workspaces, --no-git-tag-version for single-package repos. The only verb under a-novel publish is stamp, the doc-version helper prepublish:doc calls (see use-a-novel-cli).

Who can cut a release is enforced server-side, by rulesets and the environment that a-novel/infra's GitHub root manages:

  1. The master ruleset restricts who can push the version-bump commit.
  2. The tags ruleset restricts who can create a tag. A tag is the release decision: it starts the publishing run, so only admins and the release bot may create one.
  3. The release environment holds the release App's key and admits only the default branch, release/v* lines and v* tags. Every cut waits there for core or the repo's owners-<class> team; the person who dispatched may approve their own run. The first backport onto a new line waits there too, since only the release App may create the line.

These guard the release, never the registries. GitHub Packages lets every workflow run of a repository write that repository's packages, whatever its ref, so a branch can publish an npm version or push and move an image tag. A version proves its origin only through its release proof: an attestation over its bytes, signed by the producer's release.yaml at the version's tag, and an immutable Release of that tag from the release App. A branch run's attestation names its branch, and only a release makes the tag and the Release. The auto-approver merges a first-party version only with that proof (write-github-actions), and infra deploys an image only with it (write-infra).

A v* tag is only ever created by a CI dispatch — the release or release-train job, running as the release App. Nobody pushes one by hand, maintainers included. Repo admins can bypass every rule here, but that power exists to repair a broken state, never to cut a release. GitHub starts no workflow when more than three tags arrive in one push, and the cut pushes the root tag with one tag per Go sub-module, so a repository keeps fewer than three sub-modules.

Releases are human-triggered but bot-run. A human starts the release workflow; the agent never decides to publish (or merge, or push to master) on its own, and a non-interactive token must lack those rights server-side.


Hotfixing a released line

A bug sometimes must be fixed on an already-released line without shipping whatever has since landed on the default branch. That is a hotfix. The fix lands on the default branch first, through its normal PR. A backport then carries it onto the released line, and merging the backport cuts the patch. The default branch is never rewritten and never receives a backport. A hotfix is a single-repo path; a bug spanning several repos is a fast Epic (see coordinate-landing).

Nothing rolls a release back. Infra refuses a pin that lowers a version, so a bad release is fixed forward, and the hotfix is the fast path:

  • When the default branch holds nothing unreleased since the deployed release, skip the hotfix: a normal patch release ships the fix with one review less.
  • A hotfix deploys only on the deployed line or a newer one. A patch to an older line than the one infra pins reads as a lowered version, and infra refuses it.
  • A pending newer release swallows a hotfix's pin. pin-release.yaml keeps one pin pull request per repository at the newest release it saw. While a newer release's pin is still open, the hotfix folds into it and never deploys alone: close that pull request first.

Vocabulary (single-repo hotfix — freeze it here):

  • baseline — the latest live tag on the target X.Y line (e.g. v1.4.2 for line 1.4).
  • release line — release/vX.Y, created from the baseline the first time the line needs a fix. It holds released code plus backports only, and its head is always its latest patch. The release-lines ruleset protects it like the default branch: a PR, one approval, squash, and master's required checks. Only admins and the release bot write to it directly.
  • backport — a PR into a release line carrying a fix in the form that line's code needs, titled <fix subject> [vX.Y]. Its checks run against the released code, so a fix that leans on unreleased code fails before it ships.
  • cut — merging a backport runs release-core-hotfix behind the protected release environment, as the release App. It bumps the patch, then pushes the bump commit to the line together with the vX.Y.(Z+1) tag. It creates the Release with --latest=false, so a patch to an old line never becomes "Latest". The tag runs the line's own publishing jobs, so the patch reaches production, except on a line cut before its repository adopted the release split.
  • retract — for a Go module, a retract vX.Y.Z directive (added in a later release) marks a bad version so go get skips it. The tag stays (immutable); retract is the downstream "don't use this".

Ship a hotfix.

  1. Land the fix on the default branch through its normal PR.
  2. Dispatch the repo's hotfix.yaml from the UI. Only admins may run it, and dry_run defaults on. Set the X.Y line and fix_refs: the fix's default-branch commit SHAs, oldest first. Every SHA must be on the default branch, or the run fails before creating anything. Each fix becomes its own backport PR. An empty fix_refs only prepares the line, for a fix the default branch does not need: branch from release/vX.Y and open that backport by hand.
  3. A fix that does not apply cleanly opens nothing; a typical cause is a rename on the default branch that the line lacks. The escalation ticket carries the commands to write the line's form of the fix by hand: cherry-pick, resolve the conflicts against the released code, and open the PR into release/vX.Y.
  4. A clean backport, whose patch-id equals its fix's on the default branch, is opened by the Dependencies App, approved by the agent and merged once its checks pass: it carries the fix's review. A backport that git fitted to different context opens as the agent: review and merge it. The merge cuts the patch once a release environment reviewer approves. Backports merged together cut one patch.

A backport never receives a required check that was added after its line's tag, so an admin merges past that check. Nothing reminds anyone of a backport left open, so merge it once it is approved. A hotfix is deliberately exempt from AGENT_KILL_SWITCH, so the emergency patch path stays open even when normal automation is halted. The admin check and the release environment gate it instead.

Security hotfix (embargoed). When the fix can't be public before release:

  1. Work it under a draft GHSA advisory in a temporary private fork. A private fork inherits none of the org secrets or runners, so there is no CI there and nothing leaks.
  2. Coordinate the disclosure.
  3. Land the fix and its backport back to back, cut the patch, and publish the advisory at the same time.
  4. retract the vulnerable version(s) in the follow-up.

The default branch carries the fix before the patch exists, so no later release can re-ship the hole. Outward policy (contact, disclosure window) lives in SECURITY.md; the mechanics live here.


Working a cross-repo change on a branch

You're changing repo A (the dependency — e.g. golib) and repo B (the consumer — e.g. a service) together, and B's branch needs A's unreleased code. Don't add a replace; pin B to A's commit SHA, which Go records as a pseudo-version:

# In B, on B's branch, after pushing A's branch:
go get github.com/a-novel-kit/golib@<commit-sha-on-A's-branch>
go mod tidy
# go.mod now reads e.g.  github.com/a-novel-kit/golib v0.20.32-0.20260512140000-abcdef123456
  • Pin to an explicit commit SHA, not @<branch-name> — a branch ref resolves to whatever the tip is at the moment you run go get and can silently drift; an SHA is stable. (go get @main also produces a pseudo-version, just a less reproducible one.)
  • Each time you push more commits to A's branch, re-run go get …@<new-sha> + go mod tidy in B so B builds against the latest A.
  • B's CI goes red on a pseudo-version pin where the org enforces "released versions only", as it should: that is the signal B is not yet mergeable. See the ordering rule.
  • Only a draft pins a pseudo-version. A ready PR never pins an internal library to a commit, since a reviewer may approve and merge it. Before marking B ready, ask the developer to cut A's release, bring it to B's master through Renovate, and drop the pseudo-version commit as B rebases. Do not mark B ready on the pseudo-version and ask afterwards.

The merge order — dependency first, always

A pseudo-version points at a commit on a branch. Whether that commit survives on master depends on the merge strategy — a merge commit keeps it, a squash- or rebase-merge does not, and once the branch is deleted GitHub serves the orphaned commit only for a while. Either way, a PR that merges with a pseudo-version pin bakes a non-release into production: unreproducible from tags, invisible to Renovate, and a landmine once the source commit is gone. A pseudo-version pin is safe only during in-flight development, never at merge time. So:

Hard rule: the dependency PR merges and releases before the consumer PR merges, and the release reaches the consumer's master through Renovate first.

  1. Merge A's PR (the dependency change) to A's master. The release workflow tags vX.Y.Z and publishes it.
  2. Bring the release to B's master through Renovate, as the next section describes.
  3. Rebase B's branch on master and drop its pseudo-version commit. B's diff now holds only the feature, and its CI goes green on the released version.
  4. Merge B's PR. Now production is fully on released versions.

Bring a release through Renovate

A feature branch never pins a release itself. Renovate merges bumps on every consumer all day, so a pin commit in a feature branch conflicts with each one that touches the same lines. It also asks the feature's reviewer to acknowledge a dependency change, with allow-dependency-change, and mixes a version bump into a feature diff.

  1. Dispatch Renovate in each consumer once the release exists: gh workflow run renovate.yaml --repo <org>/<consumer>. Wait for its bump PR.
  2. Let a green bump PR land. Its auto-merge queues it.
  3. Fix a red bump PR on its own branch. When the release needs a code migration, such as a visual change a consumer's tests pin, push only that migration onto Renovate's branch, right after its last rebase. The push withholds the auto-approval, so the PR needs a maintainer's approval. Keep everything else for the feature branch.
  4. Rebase the feature branch on master once every bump has merged. It carries the leftovers: the feature, without a pin or a lockfile change. A rebase conflict with the migration means the feature repeated it; keep master's side.

It takes longer than a pin in the feature branch, but nothing conflicts, the feature needs no dependency label, and each review reads one kind of change.

For a pnpm consumer, a fresh first-party release installs at once: every workspace exempts the @a-novel and @a-novel-kit scopes from pnpm's minimumReleaseAge, since the release proof stands in for the wait. A fresh third-party release sits inside that age (one day by default). Exempt it by exact version in pnpm-workspace.yaml minimumReleaseAgeExclude, one entry per pinned package. Bump in two steps, because pnpm also rejects the outgoing pin when it is still too fresh:

  1. Add the new versions next to the old ones, then run pnpm install.
  2. Remove the old entries, then pass pnpm install --frozen-lockfile.

Never hand-merge pnpm-lock.yaml during a rebase. At each conflicting commit, take the upstream lockfile (git checkout --ours), then run pnpm install. Exempt only the versions pnpm names, and only while installing. Restore the commit's workspace file, and pass --frozen-lockfile before continuing. The policy relaxation never reaches a commit.

If B's change is itself a dependency of a repo C, repeat: A → release → Renovate bumps B → B releases → Renovate bumps C → C merges. Stacked dependency chains merge bottom-up.

Write the order down so it can't get lost — in B's PR description, and/or as TaskCreate entries:

1. golib#NNN  (adds X)                  → merge → release v0.20.32
2. service-foo#MMM  (uses X)            → Renovate bumps golib to v0.20.32, rebase, then merge
3. (if needed) golib#PPP  (removes old X) → after #2 ships → release v0.21.0

Edge case — A is golib, which has no separate "release PR" and releases on tag: dispatch A's release workflow once its PR merges. Don't merge B until that tag exists and B is pinned to it. If you cannot dispatch it, hand off: "golib#NNN is merged; needs a release tag before service-foo#MMM can land: dispatch Renovate there once vX.Y.Z is out."


Breaking changes are staged — never broken in one shot

A breaking change to anything other repos depend on — a golib / graduated-package public symbol, a service's pkg/go API, a proto message, a REST endpoint, a DB schema another reader relies on — is never a single PR that flips old → new. Stage it across releases, so there is always a window where both old and new work.

Step 0 — Grep the whole workspace for consumers first

Breaking changes are fine when genuinely needed — a better API, a security fix that required a signature change, an obsolete helper a newer dependency now covers. The discipline this skill enforces is knowing the blast radius before you start, so the consumer-migration step isn't a scramble.

Before writing the additive PR, enumerate every consumer of the symbol you're about to change. That grep:

  • Tells you the blast radius — one consumer is a different change than ten, and the survey costs the same either way.
  • Seeds the consumer-migration list in step 2 below, tracked explicitly so no consumer is forgotten and fails silently at step 3 (removal).
  • Surfaces the case where the "shared" symbol has zero in-org consumers, and lets you decide what that means for this particular symbol. For an internal / not-yet-public helper (a fresh golib sub-package no service imports yet, a service's private pkg/go symbol), zero consumers usually means the additive-then-deprecate dance is over-engineering and the symbol can be removed directly. For a graduated / community-facing package (a-novel-kit/jwt and any future graduate — see write-go-kit's graduation rules), the workspace grep does not see external pkg.go.dev consumers, so staged removal still applies at zero in-org hits: the discipline is the public API contract, not the consumer count. Similarly, a single in-org consumer of a golib helper may signal it belongs inlined into that consumer (write-go-kit's "should this go in golib?" bar) — a golib-specific rule, since graduated packages stay staged regardless.

Grep commands from the workspace root (the directory holding app/ and kit/), by symbol kind:

# A Go function / type / constant — name only, captures all import paths.
grep -rn "SymbolName" app/ kit/ --include='*.go'

# A proto message or RPC.
grep -rn "MessageName" app/ kit/ --include='*.proto' --include='*.go'

# A REST endpoint path or query parameter.
grep -rn 'apiPath' app/ kit/

# A renamed type — survey BOTH old and new names so consumer files using either show up.
grep -rn "HttpConfig\|HTTPConfig" app/ kit/ --include='*.go'

# A field on an exported struct (don't forget composite-literal usage).
grep -rn "\.FieldName\|FieldName:" app/ kit/ --include='*.go'

Search both app/ (services) and kit/ (other shared libraries) — a graduated-package change rips through jwt consumers as easily as service consumers. Capture the file list as a checklist in the dependency PR's description, under a heading like "Consumer migration follow-ups", so it travels with the PR through review.

Skipping this step has a specific failure mode: the dependency PR merges and releases, and weeks later someone finds a consumer nobody had on the radar — usually when it fails to compile after the removal step. Catch it now, not then.

The staged path itself

  1. Add the new path, non-breaking. New function / field / endpoint / column alongside the old, which keeps working unchanged. Release this (a feat:, not a breaking change — nothing broke yet). Mark the old path // Deprecated: use NewThing so consumers and pkg.go.dev see it.
  2. Migrate every consumer to the new path. One PR per consumer (drawn from the step-0 list), each rebased on the Renovate bump that brings the release from step 1 (per the merge-order rule), each releasing once green. Tick consumers off the list as their PRs land; an empty list means step 3 is safe.
  3. Remove the old path. Once every consumer is off it, a follow-up PR in the dependency deletes the old symbol/field/endpoint/column. This is the breaking change, now safe because nothing uses the old path. Release it (a major bump for a graduated package / pkg/go; for a service's internal-only contract, the version policy still applies).

The major-version bump in step 3 is signalling — it tells consumers "the old path is gone". It does not license skipping steps 1–2: a major bump that strands consumers is still a broken release.

This shape recurs everywhere; the layer skills carry the specifics:

  • Go public APIs (golib, graduated packages, pkg/go): add → // Deprecated: → migrate → remove. Never change a function signature in place; add a new function.
  • Proto (write-proto): never reuse a field number, never change a field's type, never remove a field that's still consumed — add new fields/messages/RPCs; mark old ones // Deprecated; remove only after every gRPC consumer is updated. buf breaking enforces a lot of this.
  • REST API (write-openapi): add new endpoints/parameters/fields; keep old ones working; document the deprecation; remove only after clients have migrated. Optional request params and additive response fields are non-breaking; removing or repurposing either is breaking.
  • DB migrations (write-sql): additive first (new nullable column, new table, new index) — and never modify a .up.sql that has merged to master; a "change" is a new migration. A destructive migration (drop column, narrow a type) ships only after the code that read the old shape is gone.

Use the BREAKING CHANGE: commit footer (git-conventions) only on step 3 — the actual removal — not on step 1.


Quick checklist

When a change spans repos:

  • Identify the dependency direction (who imports/consumes whom) and order the work bottom-up.
  • On the consumer branch, pin to the dependency's commit SHA while its PR is a draft (go get module@<sha>, then go mod tidy) — never a replace, never @branch. Before marking it ready, ask the developer to cut the dependency's release, then rebase on Renovate's bump to it.
  • Breaking a shared contract? Stage it (add → deprecate → migrate → remove); the current PR is the additive step only.
  • For any breaking change to a public symbol, grep -rn the whole workspace (app/ and kit/) for consumers before writing the dependency PR, and paste the file list into the PR description under "Consumer migration follow-ups".
  • Write the merge order in the PR description and/or TaskCreate.
  • Merge the dependency PR → wait for its release tag → dispatch Renovate in the consumer and land its bump, with only a migration pushed onto it when red → rebase the consumer branch on master, without a pin of its own → confirm CI green → merge the consumer. Repeat up the chain.
  • Never merge a PR whose go.mod (or Docker-image pin) points at a pseudo-version, a branch, or latest.

When a cross-repo landing fails — the runbook

The saga's recovery mechanics (freeze, roll-forward, rollback, the release train) live in coordinate-landing. This is the version-side decision tree: what a stuck or bad cross-repo landing means for the pins and releases, and how to unwind it. A published release is immutable, so cross-repo recovery is almost always roll forward — a new release that supersedes, rather than rewritten history.

SymptomWhat happenedDo this
Consumer PR red on "released versions only"Its go.mod still pins a pseudo-version (a dependency commit SHA), not a released tagWait for the dependency to release, land Renovate's bump to vX.Y.Z, rebase the consumer, push. Never merge on a pseudo-version.
Dependency released, consumer stuck in the Epic setPublish-before-rollout half-done: dep shipped, consumer not bumped/mergedDispatch Renovate in the consumer and rebase it on the bump; the merge-gate lands it with the set. The dep's release is immutable: roll forward, don't roll back.
An Epic partially landedINV-1 violated — a member left the queueThe sweep freezes the survivors and rolls a landable stray forward within grace. If it can't land, fix it, or (last resort) epic-rollback the landed subset. See coordinate-landing.
A bad version shipped (released, then found broken)A release is immutable — you can't un-publishRoll forward: cut a patch that reverts the change, or epic-rollback the Epic (it lands compensating reverts and re-releases). Never delete a published tag a consumer may already pin.
A tag without its GitHub ReleaseThe cut pushed the tag, then gh release create failed (GitHub answered HTTP 500)Cut the next patch from the same master. No workflow publishes a Release for an existing tag, and production deploys an image only with an immutable Release from the release App, so the tag's images never deploy. The patch's generated notes cover both versions (genai v0.10.0, then v0.10.1).
The release train left a repo un-releasedA dispatch/approval failed mid-trainRe-dispatch the train — it resumes from live tags and re-cuts only the unshipped. A parked run (release-env approval) is pending; approve it, then re-dispatch to record the receipt.
A contract-step removal broke a consumerA consumer was missed in the expand→contract migrationIts build is red against the new dep release. Fast-follow: migrate the missed consumer and release it; until then it pins the previous dep release.

epic-rollback is the one sanctioned "undo," and even it works by landing new compensating commits and then re-releasing — never by rewriting a tag.

Common pitfalls

  • replace directive for a local/branch dependency. Pin to a commit SHA instead, then rebase on Renovate's bump to the released tag before merging.
  • Merging a consumer on a pseudo-version pin. The pin must be a released vX.Y.Z at merge time; the red "released versions only" check is telling you the truth.
  • Merging the consumer before the dependency is released. Dependency PR → release → Renovate bumps the consumer → the consumer rebases and merges, in that order.
  • One PR that flips a public API / proto / endpoint / column old → new. Stage it, so there is always a both-work window.
  • BREAKING CHANGE: footer on the additive step. It goes on the removal step (step 3).
  • A major bump used as a shortcut. It signals "old path removed"; it does not excuse skipping the consumer migration.
  • A release pinned in a feature branch. It conflicts with every Renovate merge on the same lines and asks the feature's reviewer for a dependency label. Dispatch Renovate and rebase on its bump instead, as bring a release through Renovate shows.
  • Forgetting the chain. A consumer that is itself a dependency continues the dance one level up; write the whole order down.
  • Skipping the step-0 consumer grep. A consumer found after step 3 shipped is a broken build, or worse a runtime hole, downstream.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.