Skip to content

harnessed update

Mike Crowe edited this page Jul 31, 2026 · 1 revision

harnessed update — pin management

harnessed update scans the active catalog for outdated pins and offers to bump them in place. It writes the new version strings directly into recipe.yaml (for tools: entries) or reports unresolvable pins for manual review. It never builds or launches anything — verification is a separate, explicit step after accepting a bump.

What it scans

Two categories of pins are discovered across every recipe in the catalog:

Resolvable tools: entries. Each entry is resolved against its backend:

Spec prefix Backend Registry consulted
npm:<pkg>@<ver> npm registry.npmjs.org
github:<owner>/<repo>@<ver> GitHub releases api.github.com/repos/<owner>/<repo>/releases
<tool>@<ver> (bare) mise mise's own tool registry
pipx:<pkg>@<ver> PyPI pypi.org/pypi/<pkg>/json

When a newer version is available and old enough (see below), harnessed update offers to rewrite the tools: entry.

Opaque pins in scripts and Dockerfiles. Pins buried inside install.sh or a recipe Dockerfile — typically VARIABLE=v1.2.3 assignments or GitHub archive refs — are extracted by pattern but have no queryable backend. They are reported as unresolved with their current value and the file location; they are never bumped automatically. Update them by hand, then re-run harnessed update to verify nothing was missed.

The release-age gate

A release younger than the minimum age window is not offered, even if it is the newest version. This mirrors pnpm's minimumReleaseAge (same unit: minutes). The default is 7 days (10080 minutes) — chosen because a compromised or broken publish is typically yanked within days, and declining to install anything younger closes that window at zero cost.

Unlike a naive gate, a too-fresh newest release does not mean "no update": the newest version that is old enough is offered instead, and the newer-but-too-fresh version is named alongside it so you know it exists.

  tools: [npm:context-mode@1.0.169] — current
    latest: 1.0.175 (3 days old — skipped by release-age gate)
    offered: 1.0.173 (9 days old) ← this is what the command offers

Override the window with --minimum-release-age <minutes>. Pass 0 to disable the gate entirely.

Held pins

A pin can be marked hold — listed for information but never entered into the bump set and never counted by --check. Two hold sources:

  • install.hold: "<reason>" on a recipe's install: block — holds all pins fetched by that install script.
  • The map form of a tools: entry with a hold: key.

The motivating case is skill content: a skill is agent instructions executed with the agent's full tool permissions. A compromised upgrade is prompt injection, not a CVE — nothing in osv/grype/trivy detects it, and a human must read the diff before accepting it. The hold: reason string is shown alongside the newer version so whoever decides whether to lift it has the context.

Held pins are always listed, never silently dropped:

  HELD: superpowers/install.sh SUPERPOWERS_VERSION=v6.0.3 → v6.1.0
        reason: skill content — prompt injection risk; review the diff before lifting

Flags

harnessed update                     # interactive — prompts for each offered bump
harnessed update --yes               # accept every bump without prompting
harnessed update --check             # CI mode: report and exit non-zero if any pin is outdated
harnessed update --minimum-release-age 0    # disable the age gate (all newest versions offered)
harnessed update --minimum-release-age 2880 # 2-day window instead of 7

--check writes nothing to disk. It exits non-zero when any resolvable, non-held pin is behind its latest age-eligible release. CI runs this on a schedule — not as a PR gate — so a stale pin blocks no PR and is surfaced as a scheduled job failure instead (harnessed-4xu).

After a bump

harnessed update prints the exact commands to run after accepting bumps:

[INFO] Bumped: context-mode, ccstatusline
       Affected stacks: claude_superpowers, claude_time
       Verify before committing:
         harnessed build claude_superpowers && harnessed test claude_superpowers
         harnessed build claude_time && harnessed test claude_time

These commands are generated by update.affected_stacks and update.verify_commands. They cover every stack in the active catalog that includes a bumped recipe. Run them before committing; an unverified bump is worse than a stale pin.

The catalog is under the worktree → tests → PR rule (see CLAUDE.md). A bumped-pin commit is a code change like any other.

minimumReleaseAgeExclude

pnpm's minimumReleaseAgeExclude (per-package opt-out of the age gate) has no equivalent in harnessed update yet. harnessed-69k tracks the open work. For now, use --minimum-release-age 0 to bypass the gate for the whole run, or accept the offered intermediate release and update to the newest manually after inspection.

See also

Clone this wiki locally