---
name: baldart-update
effort: medium
description: "Guidance for updating BALDART framework to the latest version in a consumer repo. Use when the user says /baldart-update, 'aggiorna baldart', 'aggiorna framework', 'update del framework', 'pull baldart latest', or wants to bring the framework up-to-date. The update is SEAMLESS (v4.8.0+): a single `npx baldart update --yes` auto-stashes non-framework work, auto-resolves divergence (overlay-covered edits → clean reinstall that re-applies overlays; uncovered edits → auto-captured into overlays and verified), reconciles + commits BALDART-managed artifacts, and re-applies the stash — zero prompts in the happy path. The skill only narrates and reports; it never re-implements the CLI or replicates its prompts. Two modes, auto-detected: INTERACTIVE (a human is present) and AUTONOMOUS (unattended — CI / scheduled routine, signalled by BALDART_AUTONOMOUS / CI / GITHUB_ACTIONS env, uses --json). The CLI stops ONCE only when an edit cannot be captured+verified (a section deletion, a frontmatter/preamble change, a src/ change) — the skill surfaces that single pause. Always asserts post-flight that .framework/VERSION actually changed. For non-update drift, defers to `npx baldart` (smart doctor)."
contamination_scan: skip
---

<!--
DRIFT-CHECK
This block is read by scripts/check-update-skill-drift.js (triggered by
.github/workflows/check-update-skill-drift.yml on push tag v*.*.* and
pull_request). When any of the numbers below changes against the actual
sources, a warning is surfaced in the workflow run. The decision whether
this skill needs an update is left to a human reviewer.

update_js_prompts: 5
hook_registry_entries: 4
config_template_top_level_keys: features,git,identity,lsp,paths,stack,tools,version
last_verified: v4.8.0
-->

<!--
JSON-OUTPUT NOTE (v3.32.0)
`npx baldart version --json` and `npx baldart update --json --yes` emit a single
machine-readable object on stdout (schemas `baldart.version/1` /
`baldart.update/1`), with all human output on stderr. The skill PREFERS these
over regex-scraping prose, but keeps the text parser as a fallback for the
"new skill + old global CLI" transition window. `--json` is a CLI FLAG, not a
baldart.config.yml key — the schema-change propagation rule does NOT apply.
-->

<!--
AUTONOMY NOTE (v3.31.0)
`BALDART_AUTONOMOUS` is a RUNTIME ENV VAR, not a baldart.config.yml key — the
schema-change propagation rule does NOT apply (no template/configure/update
detector change). It is injected by the routine-adapters (github-actions: env
block; cron: `export`; claude-code-cloud: best-effort `env` field in the run
config) so scheduled routines run unattended. Any orchestrator that wants a
child agent to update autonomously can set it the same way.
-->


# /baldart-update — Update framework in this consumer

Drive a **seamless** update of the BALDART framework inside the consumer repo.
The CLI does the whole job in one shot — `npx baldart update --yes` — and the
job is always the same: **update and preserve overlays**. The skill runs that
command, watches for the single legitimate pause (an edit that can't be
captured+verified), and reports. It does NOT replicate the CLI's old
preview/stash confirmations (the CLI handles both automatically since v4.8.0)
and it NEVER re-implements the CLI's logic.

```
╔══════════════════════════════════════════════════════════════════════╗
║ BOOTSTRAP NOTICE                                                     ║
║                                                                      ║
║ IF THIS IS THE FIRST UPDATE AFTER A CONSUMER WAS ON A VERSION        ║
║ THAT PREDATES /baldart-update, THIS SKILL DOES NOT YET EXIST IN      ║
║ THE CONSUMER'S .claude/skills/. The user must run:                   ║
║                                                                      ║
║     npx baldart update                                               ║
║                                                                      ║
║ directly in their terminal **once**, and the skill becomes           ║
║ available for every subsequent update. This skill is the pattern     ║
║ for follow-up updates, not the very first one.                       ║
╚══════════════════════════════════════════════════════════════════════╝
```

## Mode detection — FIRST STEP, BLOCKING (v3.31.0+)

Before anything else, decide which of two modes you are in. This gates the
entire rest of the skill.

```bash
# A single positive signal of automation means "no human is in the loop".
# Absence of ALL signals means "human present" → stay interactive.
printenv | grep -qE '^(BALDART_AUTONOMOUS|CI|GITHUB_ACTIONS)=' && echo AUTONOMOUS || echo INTERACTIVE
```

- **`AUTONOMOUS`** → no human can answer chat prompts (scheduled routine, CI,
  or an orchestrator that exported the marker). Follow **`## Autonomous mode`**
  below. Do NOT ask any chat questions — there is no one to read them.
- **`INTERACTIVE`** → a human is present. Follow the interactive flow
  (Execution model + Workflow steps), which replicates every CLI decision
  point as a chat confirmation.

> **Why not `[ -t 0 ]`?** When Claude runs a command via the Bash tool, stdin
> is never a TTY — not even with a human present. The TTY test cannot tell the
> two situations apart. Only a *positive* automation signal (env var) is
> reliable, and we default to INTERACTIVE on its absence so a human never
> silently loses control.

## Execution model (INTERACTIVE mode) — IMPORTANT

This is an **agent-driven, seamless** skill. The flow is short:

1. Run the *read-only* pre-flight: `npx baldart version` (+ the CLI version
   drift gate). Already aligned → report "already current" and STOP.
2. Launch `npx baldart update --yes` via `Bash` (long timeout — see Hard
   rules). No chat confirmation of preview or stash: the CLI auto-stashes the
   dirty tree, shows the CHANGELOG preview itself, auto-resolves overlay-covered
   divergence by a clean reinstall, auto-captures+verifies uncovered edits, and
   auto-commits the BALDART-managed reconcile. The user authorized the seamless
   behavior by invoking the skill — do not re-ask what the CLI already handles.
3. Post-flight assert that `.framework/VERSION` changed, then report the version
   delta, backup tag, any agent-layout migration, and (if the CLI stopped at the
   single capture pause) the verbatim blockers + next step.

The ONLY time the flow is not a single shot is when the CLI exits non-zero
because an edit could not be captured+verified faithfully (section deletion,
frontmatter/preamble change, or a `src/` change). That is a deliberate,
data-safety pause — surface it; never force past it.

## Autonomous mode (AUTONOMOUS) — unattended, zero prompts (v3.31.0+)

When mode detection returned `AUTONOMOUS`, there is no human to confirm
anything. The skill drives the update end-to-end with NO chat questions, and
leans entirely on the CLI's own safety gates — which auto-resolve seamlessly
under bare `--yes` (auto-stash, overlay-covered reinstall, capture+verify of
uncovered edits) and **stop hard** only when an edit cannot be captured+verified
(`divergence-capture-incomplete`).

**The contract: autonomy over the whole seamless flow, hard STOP only on an
edit that cannot be preserved safely.**

### Autonomous workflow

1. **Pre-flight (read-only)** — same as interactive Step 0: run
   `npx baldart version`, parse the `Installed:` and `Remote:` lines with the
   tolerant matcher. Apply the same **CLI version drift gate** (a too-old
   global CLI crossing the v3.27.0 payload boundary → STOP and report; do not
   launch).
   - Aligned / already up-to-date → report "already current", exit success.
2. **Launch directly — no preview/stash confirmation. Prefer `--json`
   (v3.32.0+) so the result is a parseable object, not prose:**
   ```bash
   npx baldart update --json --yes
   ```
   Use the Bash tool with `timeout: 600000`. `--yes` implies `--auto-stash`,
   so a dirty tree is handled (stashed + re-applied) without asking. `--json`
   requires `--yes` (it refuses interactively) and routes all human output to
   stderr — **stdout carries exactly one JSON object**.
   **Do NOT add `--on-divergence pull` or `--reset`.** Bare `--yes` is the
   seamless ceiling (v4.8.0+): the CLI auto-stashes, auto-resolves overlay-covered
   divergence by a clean reinstall, and auto-captures+verifies uncovered edits.
   It exits non-zero only when an edit cannot be captured+verified faithfully
   (action `divergence-capture-incomplete`) — a correct stop that a human owns.

   **Fallback:** if the CLI predates v3.32.0 it rejects `--json` (unknown
   option, non-zero exit, empty/garbled stdout). Re-run without it
   (`npx baldart update --yes`) and fall back to exit-code + stdout-grep
   interpretation. Never let a missing `--json` block the update.
3. **Interpret the result — JSON first, exit code as the safety backstop:**
   - With `--json`, parse the single stdout object. `ok === true` (action
     `updated`, `reset`, or `already-current`) → success, go to post-flight
     using `installed_after` / `backup_tag`. `ok === false` → the CLI stopped
     or failed; read `action` (`divergence-capture-incomplete`, `failed`,
     `framework-missing`, `usage-error`), `reason`, `blockers`, and
     `next_command`. Report them verbatim. `action: 'reset'` is the seamless
     overlay-covered resolution — a normal success, not an escape hatch.
   - The **exit code is still the contract** regardless of `--json`:
     - **Exit 0** → update applied (or already aligned). Go to post-flight.
     - **Exit non-zero** → the CLI hit a wall it will not cross unattended:
       custom-overlay-able / real-custom divergence, a subtree-pull conflict, or
       a stale-CLI halt. **STOP. Do NOT retry with `--on-divergence` or
       `--reset`.** Surface the JSON `reason` + `next_command` (or, in fallback
       mode, the verbatim stderr) so the human who reads the routine log knows
       exactly what needs a hands-on decision. Forcing a merge or a reset
       unattended is precisely the silent destruction this mode forbids.
4. **Post-flight assert (MANDATORY)** — identical to interactive Step 2:
   `cat .framework/VERSION`, compare to the pre-update version. If the CLI
   exited 0 but VERSION did not change (and stdout is not "Already up to
   date"), **FAIL LOUD** — do not report success. With `--json` this is a
   direct check: the result's `installed_after` MUST equal both
   `.framework/VERSION` and the Step 0 `remote_version`; any mismatch (e.g.
   `ok:true` but `installed_after` ≠ on-disk VERSION) is the same FAIL LOUD.
5. **Report** — emit a structured, log-friendly summary (no questions):
   version delta, backup tag + rollback command, any agent-layout migration
   (v3.27.0+), and — on a non-zero exit (`divergence-capture-incomplete`) — the
   verbatim `blockers` list plus the `next_command` the CLI printed (typically
   `npx baldart update --on-divergence pull`, or author the overlay by hand).
   The skill names the options; it never picks one autonomously.

### What autonomous mode must NEVER do

- Ask a chat question (no one is there; a blocked prompt hangs the routine).
- Pass `--on-divergence pull|scaffold-overlays` or `--reset` to "get past" a
  `divergence-capture-incomplete` stop. That stop is a *correct* outcome (an
  edit that cannot be preserved safely) and needs a human.
- Report success when the post-flight assert fails.
- Auto-resolve a stash-pop or merge conflict with `--hard` / `--no-verify`.

## When to use

- The user invokes `/baldart-update`.
- The user says "aggiorna baldart", "aggiorna framework", "update del
  framework", "pull baldart latest", "porta baldart all'ultima versione".
- The user wants to bring the framework payload in the consumer repo
  in sync with upstream `main`.

## When NOT to use

- The user wants to fix drift that is NOT an update (hooks missing, config
  schema drift in isolation, broken symlinks). → `npx baldart` (smart
  doctor) is the right tool — it picks the right action among ~12.
- The consumer has no `.framework/` directory yet. → `npx baldart add`.
- The user wants to contribute local framework improvements upstream.
  → `/baldart-push`.
- The user is in the BALDART repo itself (i.e. `/Users/.../BALDART`),
  not in a consumer. This skill is for consumers; this repo updates
  via `git pull`.

## Project Context

**Reads from `baldart.config.yml`:** none directly (the skill reads
`.framework/VERSION`, `.baldart/state.json`, and `.claude/settings.json`,
which are BALDART-owned and not gated by config keys).
**Gated by features:** none.
**Overlay:** loads `.baldart/overlays/baldart-update.md` if present.
Typical overlay content: extra pre-flight checks specific to the consumer
(e.g. "block update if a release branch is open"), or post-update
verification steps (e.g. "run `npm test` after symlink reconcile").
**On missing/empty keys:** not applicable — the skill operates on BALDART's
own state files. See [framework/agents/project-context.md](../../../agents/project-context.md)
for the protocol.

## Effort

**Baseline:** `effort: medium` (frontmatter). **Inline override:** pass
`effort=<low|medium|high|xhigh|max>` anywhere in the invocation to scale
reasoning depth for this run — detect it once at kickoff and strip the token
before consuming user input. Level→behavior mapping, parsing contract, and
precedence caveats: `framework/agents/effort-protocol.md`.


## Hard rules

0. **MUST run mode detection FIRST** (the `printenv | grep` probe above) and
   branch accordingly. Defaulting to AUTONOMOUS when no signal is present —
   or to INTERACTIVE when a signal IS present — are both wrong. The default on
   *absence of signal* is INTERACTIVE (human keeps control).
1. **MUST NOT re-implement the logic of `update.js`.** Same rule as
   `/baldart-push`: the CLI is the source of truth. The skill only
   replicates prompts and parses output.
2. **MUST NOT replicate CLI prompts in chat.** The seamless CLI (v4.8.0+) has
   no happy-path prompts to replicate: it auto-stashes, shows its own CHANGELOG
   preview, auto-resolves divergence, and auto-commits. Re-asking "Procedo?" or
   "Stash?" is friction the user explicitly does not want. The only stop is the
   CLI's own non-zero exit on an uncapturable edit — surface it, don't pre-empt
   it. **(AUTONOMOUS mode) MUST NOT ask any chat question** either.
3. **MUST pass `timeout: 600000` (10 minutes)** to the `Bash` call running
   `npx baldart update --yes`. The default 120s is not enough for subtree
   pull + symlink reconcile + post-update wizard + hooks register +
   auto-commit on real-world repos.
4. **MUST NOT pass `--auto-stash`** to the CLI: `--yes` already implies it.
   Passing both is imprecise and signals a misread of the CLI.
5. **MUST report the backup tag** generated by the CLI, so the user has a
   one-line rollback path.
6. **MUST NOT launch update if `.framework/` is missing** — defer to
   `npx baldart add` and stop.
7. **MUST NOT assume defaults when a pre-check fails.** If the skill cannot
   read `.claude/settings.json` or `baldart.config.yml`, it asks the user
   how to proceed or aborts. Never silence a decision point because a
   probe failed.

## Everything the CLI auto-resolves (no chat replication) — v4.8.0+

The skill replicates **nothing**. The CLI owns the entire decision space; the
skill runs it and reports. What the CLI does on its own under bare `--yes`:

- **Preview**: prints the upstream CHANGELOG entries between the installed and
  remote versions (the old `git log HEAD..FETCH_HEAD -- .framework/` was always
  empty under subtree-squash — that dead preview is gone).
- **Working-tree stash**: a single blanket `git stash push -u` auto-stashes any
  dirty non-framework work and re-applies it after the update (or after the
  reset). Handles tracked files inside `.gitignore`d dirs — the exact case the
  old manual workaround failed on.
- **Divergence** (consumer commits on `.framework/`): classified by the CLI.
  `all-noise` (subtree plumbing / collaboration merges / CLI `[CHORE]`) →
  silent. `overlay-able` where every edit is already covered by an overlay →
  clean reinstall (`--reset`) that re-applies the overlays. `overlay-able`
  uncovered → **auto-captured into populated overlays and VERIFIED** (the merge
  must reproduce the edit) before the reinstall. `mixed` / `real-custom` (a
  `src/`, CHANGELOG, … change) → the overlay-able subset is captured, then the
  CLI **stops once** (action `divergence-capture-incomplete`) because non-overlay
  paths can't be auto-resolved.
- **Hooks drift**, **schema config drift**, **BALDART-managed auto-commit**,
  **CLI self-upgrade** (`npx baldart@latest` relaunch) — all silent as before.

Explicit overrides still exist for power users / CI (`--on-divergence
pull|scaffold-overlays|abort`, `--reset --yes --i-know`) but are NOT the path
the skill or the user needs anymore — the default IS the seamless path.

## Workflow (INTERACTIVE mode)

> In AUTONOMOUS mode follow `## Autonomous mode` instead. All three steps below
> (pre-flight, run, post-flight) are shared by both modes — the only difference
> is autonomous uses `--json` and never prints to chat. There are NO chat
> confirmation steps anymore: the update is seamless.

### Step 0 — Pre-flight (read-only)

```bash
# One call gives installed version, remote version, alignment status, and
# the BALDART repo URL. NO git operation inside .framework/ — that
# directory is a git subtree, not a separate repo.
npx baldart version
```

#### Preferred: structured `--json` (v3.32.0+) — NO regex

As of v3.32.0 the CLI emits a machine-readable object — read fields instead
of scraping prose:

```bash
npx baldart version --json 2>/dev/null
# → {"schema":"baldart.version/1","installed":true,"installed_version":"3.32.0",
#    "remote_version":"3.33.0","aligned":false,"offline":false,"fetched":true,
#    "uncommitted_files":0,"cli_version":"3.32.0","cli_latest":null,
#    "cli_update_available":false,"repository":"https://github.com/antbald/BALDART",...}
```

Decision logic on the JSON:
- `installed === false` → framework not installed → STOP, instruct `npx baldart add`.
- `aligned === true` → already up-to-date (`installed_version`). STOP.
- `aligned === false` AND `remote_version !== "unknown"` → continue (save
  `installed_version` for the post-flight assert, `remote_version` as target).
- `offline === true` / `fetched === false` → ask the user how to proceed.
- Still apply the **CLI version drift gate** below using `cli_version` /
  `cli_latest` (same semver logic).

**Fallback (BLOCKING for the transition window):** if `version --json` errors
(non-zero exit, unknown-option message, or stdout that is NOT valid JSON), the
global CLI predates v3.32.0. Do **not** abort — fall back to the **Parser
tollerante** below, which scrapes the human-readable output. This is the same
"new skill + old global CLI" case the dual-format text parser already exists
for. Never remove the text parser.

**Parser tollerante (fallback)** — the output format changed in v3.26.0 (version-compare
authority instead of subtree-merge commit count). The skill must accept BOTH
the new and legacy formats so it works during the transition window where
the consumer has the new skill but an older global CLI.

Match the `Remote:` line in this order (first match wins):

| # | Pattern (regex)                                              | Meaning                                                            |
|---|--------------------------------------------------------------|--------------------------------------------------------------------|
| 1 | `Remote:\s+v(\S+)\s+\(aligned\)`                             | NEW — aligned, save `remoteVersion` for post-flight                |
| 2 | `Remote:\s+v(\S+)\s+(?:—\|--)\s+update available`             | NEW — NOT aligned, save `remoteVersion`                            |
| 3 | `Remote:\s+(\d+)\s+commit\(s\)\s+ahead`                      | LEGACY — count > 0 means NOT aligned (subtree-merge noise possible)|
| 4 | `Remote:\s+up to date`                                       | LEGACY — aligned                                                   |
| 5 | `Remote:\s+\(offline.*\)`                                    | unknown — ASK the user how to proceed                              |

Always also capture:
- `Installed:\s+v(\S+)` → `installedVersion` (saved for post-flight assert).
- `CLI:\s+v(\S+?)(?:\s+→\s+v(\S+)\s+available)?` → `cliVersion` (current CLI binary)
  + optional `cliLatest` (newest npm version).

**CLI version drift gate (BLOCKING)** — added in v3.27.1 after a real
incident in `mayo`: a consumer on CLI v3.24.0 ran update against payload
v3.27.0 and the v3.27.0 symlink-indirection migration silently did NOT
trigger (the running `update.js` was the v3.24.0 binary, predating both
auto-relaunch and the migration logic). The CLI v3.26.0+ self-relaunches
via `npx baldart@latest`, but pre-v3.26.0 binaries do not — chicken-and-egg.

Decision logic (apply after parsing):

| If… | Then… |
|---|---|
| `cliVersion >= 3.26.0` AND `cliLatest` present | Tell once: "Il CLI globale è v<cliVersion>, ne esiste v<cliLatest>. L'update si autorelancerà via `npx baldart@latest` — niente da fare." Continue. |
| `cliVersion < 3.26.0` AND `installedVersion >= 3.27.0` (already on the new payload but old binary) | **HALT**. Tell the user: "Il CLI globale (v<cliVersion>) è troppo vecchio per applicare la migration symlink-indirection di v3.27.0 (introdotta v3.26.0+ self-relaunch e v3.27.0 symlink-indirection). Lancia `npm i -g baldart@latest` PRIMA di ri-eseguire /baldart-update, altrimenti `update.js` non vedrà la nuova logica e i 6 agent overlay-merged resteranno file regolari invisibili a Claude Code (bug #20931)." Do not launch `npx baldart update`. |
| `cliVersion < 3.26.0` AND `installedVersion < 3.27.0` AND update is going to bring the payload to `>= 3.27.0` (parse the remoteVersion from Step 0) | **HALT** with the same message — the payload will cross the v3.27.0 boundary during this update, and the running `update.js` will lack the migration logic. |
| Otherwise | Continue normally. |

Semver compare: split on `.`, compare numerically. Treat any non-numeric
segment (e.g. `3.27.0-rc1`) as "less than" the same major.minor.patch
without suffix only when strictly required; if unsure, ASK the user.

**Decision after parsing**:

- Patterns 1 or 4 → "Already up-to-date — version v<installedVersion>". STOP.
  Do not launch the CLI.
- Patterns 2 or 3 → continue with Step 1 (run the CLI).
- Pattern 5 → ask the user: "Vuoi forzare un fetch e ritentare, o procedere
  in modalità offline (l'update fallirà nel CLI)?". STOP if unclear.
- **No pattern matches** → the CLI output format is unknown. Tell the user:
  "Il CLI installato globalmente non emette un format riconosciuto.
  Aggiorna il CLI con `npm i -g baldart@latest` e ri-invoca /baldart-update."
  STOP.

If `.framework/` does not exist, `npx baldart version` will say "framework not installed" — STOP and instruct `npx baldart add`. Do not attempt to recover.

### Step 1 — Run the CLI (seamless, one shot)

```bash
npx baldart update --yes
```

**Use the Bash tool with `timeout: 600000`** (10 minutes). Capture stdout and
stderr — both are needed for Step 2. Do NOT pass `--auto-stash` (redundant with
`--yes`), `--on-divergence`, or `--reset`: the seamless default already does the
right thing. There is no chat confirmation before this — running the skill IS the
authorization (the user asked to update; the CLI has a backup tag + rollback for
safety).

Everything the CLI handles on its own is listed in **"## Everything the CLI
auto-resolves"** above. The one outcome that is NOT a clean success is a non-zero
exit with action `divergence-capture-incomplete`: an edit could not be
captured+verified faithfully. Do NOT retry with `--on-divergence` or `--reset` to
force past it — surface the verbatim blockers + `next_command` and stop (Step 2
report covers this).

### Step 2 — Post-flight assert + Report

**Post-flight assert (MANDATORY — safety net against silent CLI bugs)**:

```bash
# Read the new installed version
cat .framework/VERSION
```

Compare against `installedVersion_before` saved in Step 0:

- **If `before === after` AND stdout DOES NOT contain "Already up to date"** →
  the CLI exited 0 but the framework didn't actually update. **FAIL LOUD**:
  ```
  ✗ Update CLI exited 0 but .framework/VERSION did not change.
    Before: v<before> | After: v<after>
    This is a CLI bug — please report the full stdout/stderr above.
    The repo is in an inconsistent state; do NOT continue assuming success.
  ```
  Do NOT report success. Do NOT mark the task complete.
- **If stdout contains "Already up to date" but Step 0 had a `remoteVersion`
  different from `installedVersion`** → same FAIL LOUD (this is the v3.24.x
  bug regression check — Fix 1 of v3.26.0 must not regress).
- Otherwise → success path.

Then extract the post-update facts and surface them:

```bash
# Backup tag (most recent baldart-pre-update-* tag)
git tag -l 'baldart-pre-update-*' --sort=-creatordate | head -1

# Stash-pop conflicts (if any)
# Grep stdout/stderr for: CONFLICT, "stash pop failed", "unmerged paths"
# If matches found, list the conflicted paths:
git status --porcelain | grep '^UU'
```

Report to the user, in order:

1. **Version**: `vX.Y.Z → vA.B.C` (delta — both from `cat .framework/VERSION`
   pre and post).
2. **Backup tag**: the `baldart-pre-update-<timestamp>` ref, with the
   rollback command: `git reset --hard <tag>` (only if needed).
3. **CLI self-upgrade**: if stdout mentions "Auto-relaunching via npx
   baldart@…", surface it once so the user knows the CLI bumped too.
4. **Hooks reconciled**: any hook backfilled (parse "Auto-registered N
   missing hook(s)" or per-line "Registered hook" from stdout). Since v3.33.0
   the CLI also runs a **post-flight hook assert** at the end of `add` (and
   therefore of `update --reset`, which delegates to `add`): it re-reads
   `.claude/settings.json` and refuses to declare success — exiting non-zero
   with "Install is NOT complete" — if any active `HOOK_REGISTRY` entry is
   missing. So a clean `--reset` can no longer leave a half-install that
   reports "Valid" (the mayo 3.28→3.31 / v3.27.1 failure class). If you see
   that error, the repo is in an inconsistent state: run `npx baldart doctor`
   to repair, do NOT report success.
5. **Agent layout migration (v3.27.0+)**: parse stdout for lines matching
   `agent generated (overlay applied): .claude/agents/<name>.md → ../../.baldart/generated/agents/<name>.md`.
   Pre-v3.27.0 overlay-merged agents (`coder`, `ui-expert`, `code-reviewer`,
   `doc-reviewer`, `qa-sentinel`, `security-reviewer` when overlayed) lived
   as regular files in `.claude/agents/` and were silently invisible to
   Claude Code session-start discovery (upstream bug #20931). The CLI now
   stores merged content in `.baldart/generated/<kind>s/<name>.md` and
   places a symlink at `.claude/agents/<name>.md`. Report the migrated
   names so the user knows they should restart their CC session for the
   new layout to take effect on `subagent_type` resolution.
6. **Schema notes**: any missing key still flagged → suggest
   `npx baldart configure`.
7. **Stash-pop status**: clean / conflicts present (with file list).
8. **Suggested next step**: if schema drift residual or other non-update
   doctor checks fired, suggest `npx baldart` (smart doctor). Also remind:
   if any agent was migrated (step 5), **restart the Claude Code session**
   so the discovery scanner sees the new symlinks.

## Failure modes

| Symptom                                                | Cause                                              | What you do                                                                                                                                                  |
|--------------------------------------------------------|----------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `git -C .framework fetch` errors out                   | Offline / auth issue / repo URL changed            | STOP. Surface the error verbatim. Ask the user to retry when network is back. Do not launch update.                                                          |
| Working tree dirty + user refuses stash                | Authoring decision                                 | STOP cleanly. Instruct: commit or stash manually, then re-run `/baldart-update`.                                                                              |
| `update.js` exits non-zero (subtree pull conflict)     | Upstream and local both modified the same file     | Surface the CLI error verbatim. Provide the backup tag + `git reset --hard <tag>` rollback command. Do NOT attempt automatic conflict resolution.            |
| Stash-pop conflicts after the subtree pull             | User-modified file collides with framework change  | Detected by grep on stdout/stderr (`CONFLICT`, `stash pop failed`, `unmerged paths`). List `git status --porcelain | grep '^UU'` paths. Instruct manual resolve. NEVER auto-resolve with `--hard` or `--no-verify`. |
| `.framework/` missing                                  | Consumer never installed BALDART                   | STOP. Instruct `npx baldart add`. Do not improvise.                                                                                                          |
| Bash 10-minute timeout exceeded                        | Very slow network / disk / huge repo               | Surface the timeout. Instruct the user to run `npx baldart update` manually in their terminal to see live progress.                                          |
| Pre-check probe fails (e.g. `settings.json` malformed) | Consumer state inconsistent                        | STOP. Surface the probe failure. Ask the user whether to proceed without that pre-check, or to fix manually first. Never silently assume "no drift".         |

## Why this skill exists

The smart doctor (`npx baldart`, no args) already proposes an update
when it detects remote ahead — but it does so as **one of about twelve**
possible actions (install, configure, migrate, repair symlinks, register
hooks, reconcile state ledger, …). When the user knows they want *only*
to update, going through the doctor is more friction than signal.

This skill is the direct path for that case: a single seamless `npx baldart
update --yes` that updates and preserves overlays with zero friction, plus a
mandatory post-flight assert and a clean report. The user no longer trades
control for friction — the CLI's backup tag + rollback and its
capture+verify safety pause keep the work safe without a wall of prompts.

`/baldart-push` and `/baldart-update` together form the conversational
spine of BALDART maintenance:

- `/baldart-update` — pull from upstream.
- `/baldart-push` — push to upstream.

Both wrap the CLI without re-implementing it; the CLI remains the source of
truth. `/baldart-push` always keeps the user in the loop. `/baldart-update`
is seamless in both modes; when run unattended (autonomous — CI / scheduled
routine) it uses `--json`, but in both it leans on the CLI's safety gates,
stopping hard rather than forcing any
destructive resolution a human would otherwise own.
