Skip to content
Back to skills

Update

ASecurity

Use when deploying updates to this machine: pull, sync deps, verify, restart services. Triggered by 'update', 'deploy', 'pull and restart', or after git pull.

  • 26 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 2, 2026
ai-agentspythongoshellbashnodedebugginggit

Works with

  • terminal
  • cli
  • mcp

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 3 files and shows the line behind each finding

Scanned September 23, 2026

npx -y skills add tomcounsell/ai --skill update --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Update?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Update
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tomcounsell-update/badge)](https://www.skillsdirectory.com/skills/tomcounsell-update)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: update
description: "Use when deploying updates to this machine: pull, sync deps, verify, restart services. Triggered by 'update', 'deploy', 'pull and restart', or after git pull."
disable-model-invocation: true
---

# Update & Restart

Bring this machine to latest main: deps synced, environment verified, services restarted and healthy. The orchestrator does the work — your job is to run it, read its output, and report every warning or error.

## Instructions

**PREREQUISITE: Must be on latest main branch before running.**

```bash
cd ~/src/ai && git checkout main && git fetch origin main && git merge --ff-only origin/main
```

Fetch-then-merge-a-named-ref rather than `git pull` (#2650): `.git/FETCH_HEAD` is shared by every worktree of the repo, so with concurrent lanes on this machine a peer's fetch can retarget a bare pull's merge.

If there are local changes, stash them first: `git stash`. The update orchestrator also handles this, but being on main is required.

Run the full update orchestrator and report the results:

```bash
cd ~/src/ai && .venv/bin/python scripts/update/run.py --full
```

The orchestrator will:
- Pull latest changes (with automatic stash/unstash)
- Sync `.claude` hardlinks and audit skill hooks
- Check for pending critical dependency upgrades; sync dependencies if pyproject.toml changed; verify critical versions
- **Verify machine identity** — reads `scutil --get ComputerName`, matches against the `machine` field in `~/Desktop/Valor/projects.json`, reports which projects this machine handles
- Check/pull Ollama summarizer model; verify CLI tools and SDK authentication
- **Validate `projects.json` (green-light gate)** — runs `bridge/config_validation.py::validate_projects_config` over the full config (Step 4.6). Enforces that every bridge-contact identifier (DM contact id, Telegram group, email contact, email domain wildcard) resolves to exactly one machine. On failure: log the error, skip the service restart, leave the running bridge serving on the previously-validated config. See [Single-Machine Ownership](../../../docs/features/single-machine-ownership.md).
- Install/restart services (skipped if the validation gate failed): bridge, worker, the reflection-scheduler subprocess (`com.valor.reflection-worker` — installed right after the worker so at most a brief zero-scheduler window; issue #1828), nightly-tests, caffeinate, and log-rotate
- Set up global calendar hook and generate config; verify MCP server registration (memory Step 4.8, BYOB Step 4.9 — idempotent, self-healing)
- **Best-effort agent-judgment catchup (strictly last step)** — see below

After running, report the result. List every warning or error clearly.

**Warning channel (#2845):** the `--cron` summary format (`up to date at <sha> (N warnings)` / `update failed at <sha>` plus their bullets) is what both the Telegram reply and any auto-spawned fix session detect against — not a handful of legacy prefixes. `gws auth` and `env-completeness` warn once per state transition rather than every cycle (via `warn_state`); when any of those are currently suppressed, a `suppressed: ...` line appears in the reply naming them, with `python -m scripts.update.warn_state` and a full (non-`--quick`) `python -m tools.doctor` run as the on-demand answers for what is suppressed right now. See [`/update Warning Channel`](../../../docs/features/update-warning-channel.md) for the full producer/consumer contract.

**First-install backfill reminder (markitdown):** the first run that installs the `[knowledge]` extra appends a one-time tip to the Telegram summary: `run 'valor-ingest --scan ~/work-vault/' to backfill existing binary files into sidecars` (gated by `~/.cache/valor/markitdown-backfill-reminded`). If the user asks why pre-existing vault PDFs/docs aren't indexed, point them at that command — the watcher only picks up files modified after it starts.

**Log rotation:** every `--full` run installs the user-space LaunchAgent `com.valor.log-rotate.plist` (content-idempotent, no sudo) which rotates any `logs/*.log` over 10 MB every 30 minutes. A stale root-era `/etc/newsyslog.d/valor.conf` is removed via non-interactive `sudo -n rm`; if sudo needs a password, cleanup is skipped with a warning and retried next run.

**Obsolete launchd-job sweep (Step 1.56):** every run boots out and deletes LaunchAgents for features that have been fully removed from the codebase, so a deleted feature's plist doesn't keep loading and failing on already-provisioned machines forever. The list of removed jobs lives in `scripts/update/service.py::OBSOLETE_SERVICE_SUFFIXES` — this is the launchd analog of `RENAMED_REMOVALS` in `hardlinks.py`: **when you delete a launchd-backed feature (its install script + code), add its label suffix there** so `/update` cleans up the stale job on every machine. The sweep is idempotent and fail-soft (a machine that never had the job is a no-op); removals are logged and reported in the run summary. Runs unconditionally alongside the plist-PATH heal (Step 1.55), not gated on the service-restart step.

**Session cleanup (Step 5.5):** corrupted sessions are deleted and indexes rebuilt; running/pending sessions older than 120 min with no live process transition to `killed`; terminal sessions are preserved for reflections (which handles its own 90-day expiry).

### Agent-Judgment Catchup (strictly last step)

The **final** action of `run_update` invokes the `valor-catchup` CLI (issue #1709, see [Agent-Judgment Catchup](../../../docs/features/agent-judgment-catchup.md)): it reads each owned chat's recent thread, asks an LLM judge which inbound human messages are genuinely unanswered, and enqueues recovery sessions only for those.

**Best-effort contract — the step never blocks or fails `/update`:**

- **Health gate.** Runs only when BOTH bridge and worker report running AND `do_service_restart` is set (verify-only and follower-skip runs never trigger recovery enqueues). Otherwise it logs `catchup: skipped — ...` and returns.
- **Subprocess + tight timeout.** Runs as a subprocess with `CATCHUP_STEP_TIMEOUT_SECONDS` (90s); a hung Telethon connect or stalled LLM call is killed on expiry.
- **Failure swallowed.** Any failure, non-zero exit, or timeout is logged (`catchup: ... (swallowed)`) and never flips `UpdateResult.success`.

The CLI propagates via `pip install -e .` during the dependency-sync step (`[project.scripts]` entry) — no extra wiring.

### Auto-Bump Critical Dependencies

Auto-bump works in **coupled sets** — packages that move together or not at all — declared as `AUTO_BUMP_SETS` in `scripts/update/deps.py`. Only the lockfile-maintainer machine runs auto-bump; others skip to avoid racing.

Two sets are declared: `anthropic` + `pydantic-ai-slim` (currently **held** on `#3001 Step 2`, so it is skipped and the hold is recorded), and `claude-agent-sdk`.

Per set, every run:

1. Skips the set if it carries a `hold`, recording `held: <reason>`.
2. Resolves the latest PyPI version for **every** member. One unresolvable member skips the whole set.
3. Skips the set if no member's pin actually changed — a quiet cycle never re-resolves the lockfile.
4. Snapshots `pyproject.toml`, then rewrites **all** changed member pins.
5. Runs `uv sync` (unfrozen) to re-resolve and install.
6. Runs the set's declared gate phases in order, stopping at the first failure and naming it:
   - `llm` — `{venv}/bin/python -m agent.llm.compat --json --allow-network` (the `anthropic` + `pydantic-ai` pair only; makes one real billed call)
   - `import` — imports the set's own `import_names`
   - `pytest` — `pytest tests/unit/test_docs_auditor_substrate.py -x -q`
7. All gates pass: the set's pins survive and are committed and pushed.
8. Any gate or the sync fails: restores **that set's own** snapshot, re-syncs, records `rolled_back` plus the failed phase, and moves to the next set. If the restore's own re-sync fails, `restore_failed` is set and nothing is committed that run.

Separately, **every** `/update` and `/update --cron` on **every** machine runs a verify-time LLM stack compat check (`python -m agent.llm.compat --json` in the project venv) and reports it as the `llm-stack-compat` tool check. That leg covers hand-staged pins and follower machines, which never auto-bump. See [LLM Stack Compat Gate](../../../docs/features/llm-stack-compat-gate.md).

### Critical Dependency Handling (git-driven changes)

When `pyproject.toml` changes via git pull with critical dep version changes (telethon, anthropic, claude-agent-sdk), the cron job (`remote-update.sh`) writes `data/upgrade-pending`; running `/update` manually applies the upgrade with proper verification.

If `data/upgrade-pending` exists:
```bash
# Check what's pending
cat ~/src/ai/data/upgrade-pending

# After /update applies the upgrade and verifies the bridge starts:
rm ~/src/ai/data/upgrade-pending
```

### Verification Only

To check the environment without making changes:
```bash
cd ~/src/ai
.venv/bin/python scripts/update/run.py --verify
```

### Reinstall Launchd Services

After update, reinstall launchd plists to pick up any template changes. The reflection scheduler runs in its own launchd subprocess (`python -m reflections`, label `com.valor.reflection-worker`) — out-of-process from the worker since issue #1828 — so two installers cover the set:

```bash
cd ~/src/ai
./scripts/install_worker.sh
./scripts/install_reflection_worker.sh
```

`install_reflection_worker.sh` self-gates on worker role (any project's `machine` matches this host; fail-open) and removes its stale plist on non-worker machines. Verify with `python -m reflections --dry-run` (loads the registry, exits 0) and `tail -f logs/reflection_worker.log`. Install scripts substitute `__PROJECT_DIR__` and `__HOME_DIR__` placeholders, so plists work on any machine without hardcoded usernames.

## When to Load Sub-Files

| Sub-file | Load when... |
|----------|-------------|
| `references/troubleshooting.md` | An update run fails or the environment misbehaves afterward (venv, deps, calendar, machine identity, bridge/worker/reflection-scheduler won't start) |
| `references/modules.md` | Debugging orchestrator internals or calling a `scripts/update/` module directly (git.py, deps.py, verify.py, gws_auth.py, calendar.py, MCP registration, service.py) |

## Node toolchain (soft prerequisite)

Machines that run the `do-design-system` skill also need Node + npm (for `npx @google/design.md`). `remote-update.sh` runs `npm ci --omit=dev` guarded by:

```bash
if [ -f "$PROJECT_DIR/package.json" ] && command -v npm >/dev/null 2>&1; then
    ( set +o pipefail; cd "$PROJECT_DIR" && npm ci --omit=dev ) \
        || echo "[update] npm ci failed (non-fatal); continuing"
fi
```

The non-pipefail subshell + `|| echo` trailer guarantee a missing `npm` or a transient install failure never aborts the parent update. Machines without Node skip the block silently; design-system tooling falls back to Python-only emission (`--generate --no-node`). Lint and DTCG / Tailwind exports still require Node. See `docs/features/design-system-tooling.md`.

Files in this skill

  • SKILL.md9.6 KB
  • references/modules.md5 KB
  • references/troubleshooting.md2.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.