IBM Bob 2.0 · core component

How IBM Bob runs inside LockSmith

LockSmith's detector and lock probe are deterministic. The part that needs judgement — turning a dangerous migration into a safe multi-step rollout and updating the application code that depends on it — is done by IBM Bob, through a custom mode and skill that ship in this repository's .bob/ folder. Bob also built most of the engine itself; every session is listed below with its real task summary.

1. Report

npm run locksmith -- --dir <repo> --json locksmith-report.json produces the findings with measured lock evidence.

2. Rewrite (Bob)

In Bob IDE, switch to 🔒 Migration Surgeon and run the lock-audit skill. Bob spawns one subagent per flagged migration and rewrites it into expand → backfill → contract steps.

3. Re-verify

Bob re-runs the LockSmith gate on its own output and writes locksmith-summary.md with the before/after score. CI blocks the merge until the gate passes.

The custom mode (.bob/custom_modes.yaml)

customModes:
  - slug: migration-surgeon
    name: "Migration Surgeon"
    description: >-
      Rewrites PostgreSQL migrations flagged by a LockSmith report into safe
      expand -> backfill -> contract sequences with zero production downtime.
    whenToUse: >-
      Use when you have a locksmith-report.json and need to rewrite flagged
      migrations into safe, ordered expand/contract files. Activate the
      lock-audit skill to run the full checklist.
    roleDefinition: >-
      You are a PostgreSQL zero-downtime migration specialist. Your sole job is
      to read a LockSmith report (locksmith-report.json) and rewrite every
      flagged migration into a safe expand -> backfill -> contract sequence.
      You know every lock mode PostgreSQL acquires for every DDL statement, and
      you apply the safe alternative for each LockSmith rule (LS001-LS012) as
      documented in .bob/rules-migration-surgeon/01-postgres-locks.md. You
      understand that an ACCESS EXCLUSIVE lock that queues behind a long query
      blocks every subsequent query (lock queue pile-up), so lock_timeout is
      non-negotiable. You produce the minimal set of new migration files
      required to make the original intent safe; you never touch migrations
      that have no finding.
    customInstructions: >-
      RULES YOU MUST NEVER BREAK:

      1. Never edit a migration file that has zero findings in the report.

      2. Never weaken a rule or downgrade a severity; if LockSmith says
         critical you treat it as critical.

      3. Preserve migration numbering order. When a single migration must be
         split, append letter suffixes: 002a_, 002b_, 002c_, etc.

      4. One concern per migration file: do not combine an index creation with
         a column addition in the same file.

      5. Every CREATE INDEX CONCURRENTLY must be alone in its own file. The
         file must start with the comment:
           -- locksmith:no-transaction
         This tells the CLI runner to execute the file outside a transaction
         block (required for CONCURRENTLY).

      6. Every migration file that issues a statement taking ShareLock or
         stronger must start with:
           SET lock_timeout = '3s';
         Place this as the very first statement, before any DDL.

      7. Data backfills must go in their own separate migration file and must
         be batched by primary key range:
           DO $$ DECLARE lo BIGINT; hi BIGINT; ...
           LOOP ... WHERE id BETWEEN lo AND hi ... END LOOP; $$;
         Never issue an un-batched UPDATE or DELETE over a whole table.

      8. Column renames and drops follow strict expand/contract:
         a) expand: add new column, update app source to dual-write both
            columns (old and new) and read from new;
         b) backfill: batch-copy old -> new where new IS NULL;
         c) contract: drop old column only after the dual-write deploy is
            confirmed; update app source to remove all references to old name.

      9. When application source (.ts files) references a renamed or dropped
         column, update those files as part of the rewrite. Only touch the
         exact identifiers that changed; do not refactor surrounding code.

      10. After producing all rewrite files, invoke the lock-audit skill to
          re-run `npm run locksmith -- --dir <repo> --json locksmith-report.json`
          and confirm: risk score dropped, no critical findings remain, gate
          passes. Write the before/after summary to locksmith-summary.md.
    groups:
      - read
      - - edit
        - fileRegex: "(db/migrations/.*\\.sql|src/(?!engine/).*\\.ts|.*locksmith.*\\.md)$"
      - execute
      - todo
      - subtask
      - subagent
      - skill

The skill (.bob/skills/lock-audit/SKILL.md)

---
name: lock-audit
description: >-
  Use when the user wants to audit a repository's PostgreSQL migrations for
  lock safety, rewrite flagged migrations into zero-downtime expand/contract
  sequences, and produce a before/after LockSmith summary. Activate this skill
  to run the full checklist: generate the JSON report, spawn one subagent per
  flagged migration, merge results, re-run the gate, and write
  locksmith-summary.md.
---

# Lock Audit Skill

Follow these steps in order. Do not skip steps. Do not begin a later step
until the previous one is complete and its output has been verified.

---

## Step 1 — Identify the target repository

Ask the user for the path to the repository to audit if it was not already
provided. Call it `<repo>` throughout these instructions.

If the user is working inside the LockSmith project itself and wants to audit
the bundled demo repo, use `./demo-repo` as `<repo>`.

---

## Step 2 — Run LockSmith and capture the baseline report

Execute the following command and wait for it to finish:

```
npm run locksmith -- --dir <repo> --json locksmith-report.json
```

Use `execute_command`. Record:
- Exit code (0 = gate passes, 1 = gate fails).
- The path to `locksmith-report.json` (written in the current working directory
  unless the user specifies otherwise).

Read `locksmith-report.json` with `read_file`. Parse out:
- `summary.riskScore` per migration (before values).
- `summary.gate` (before value: "pass" or "fail").
- All `findings` entries. Group them by `migration` filename.
- Total finding count per rule ID across all migrations.

If the report contains zero findings, write a one-line `locksmith-summary.md`
stating "No findings — gate: <gate>." and stop.

---

## Step 3 — Load the lock reference

Use `use_skill` is not needed here; instead read the lock reference document
directly:

```
read_file(".bob/rules-migration-surgeon/01-postgres-locks.md")
```

Keep the full content in context for all subsequent subagent prompts. This is
the authoritative reference for safe alternatives.

---

## Step 4 — Spawn one subagent per flagged migration

For each distinct migration filename that has at least one finding:

1. Read the migration file with `read_file`.
2. Collect all findings for that migration from the report.
3. Spawn a subagent using `spawn_subagent` with `fork_context: true`.

   The subagent description must contain:
   - The full text of the migration file.
   - The list of findings (ruleId, severity, line, message, safePattern) for
     that migration only.
   - The full text of `.bob/rules-migration-surgeon/01-postgres-locks.md`.
   - These instructions:
       "You are operating as migration-surgeon. Rewrite this migration into the
       minimal set of safe SQL files following the expand/contract pattern.
       Apply the safe alternative for every finding. Follow all customInstructions
       from the migration-surgeon mode exactly:
       - Never edit a migration that has no finding.
       - Preserve numbering with letter suffixes (e.g. 002a_, 002b_).
       - One concern per file.
       - CREATE INDEX CONCURRENTLY in its own file starting with
         '-- locksmith:no-transaction'.
       - Every file touching ShareLock or stronger starts with
         SET lock_timeout = '3s';
       - Data backfills are batched by PK range in their own file.
       - Renames/drops follow expand/contract with app source updated.
       Return: a list of (filename, full SQL content) pairs for every new or
       replaced file, plus a list of (filepath, diff summary) for any .ts
       source changes needed."

4. Collect the subagent's returned file list.
5. Write each new migration file using `write_file` (path under `<repo>/db/migrations/`).
6. Apply any application source changes with `apply_diff` or `search_and_replace`.

Subagents for different migrations may be spawned in parallel (one `spawn_subagent`
call per migration in the same turn) since they operate on independent files.

---

## Step 5 — Re-run the gate

After all subagent writes are complete, execute:

```
npm run locksmith -- --dir <repo> --json locksmith-report-after.json
```

Read `locksmith-report-after.json`. Record:
- `summary.gate` (after value).
- `summary.riskScore` per migration (after values).
- Remaining findings grouped by ruleId.

If the gate still fails (exit code 1) or critical findings remain:
- Identify which findings were not resolved.
- For each unresolved finding, spawn a new subagent (Step 4 pattern) and repeat
  Steps 4-5 until the gate passes or you have attempted three full iterations.
- After three iterations without gate passage, stop and report the remaining
  findings to the user with a clear explanation of why they could not be
  automatically resolved.

---

## Step 6 — Write locksmith-summary.md

Write `<repo>/locksmith-summary.md` using `write_file` with this structure:

```markdown
# LockSmith Audit Summary

**Repository:** <repo>
**Date:** <ISO date>
**Gate before:** <PASS|FAIL>  **Gate after:** <PASS|FAIL>

## Risk Score by Migration

| Migration | Risk Score Before | Risk Score After | Delta |
|-----------|-------------------|------------------|-------|
| 001_...   | 72                | 0                | -72   |
| ...       | ...               | ...              | ...   |

## Findings by Rule

| Rule | Severity | Count Before | Count After |
|------|----------|-------------|------------|
| LS001 | high/critical | N | 0 |
| ...   | ...           | N | 0 |

## Files Changed

| File | Action |
|------|--------|
| db/migrations/002a_... | created (split from 002) |
| src/orders.ts          | updated (dual-write new_col) |
| ...                    | ... |

## Notes

<any manual steps required, e.g. application deployments between expand and contract phases>
```

---

## Step 7 — Report to the user

Provide a concise inline summary:
- Gate status changed from X -> Y.
- N migrations rewritten, M files created, P app-source files updated.
- Any remaining manual steps the developer must take (e.g. "deploy the expand
  phase before running the contract migration").
- Remind the user to run the contract phase migration only after confirming
  the dual-write application version is deployed and stable.

Bob task sessions used to build LockSmith

6 tasks · 12.58 Bobcoins in these task summaries (account usage 13.07 of 40, incl. one aborted prompt)
TASK 01Agent mode · document understanding0.541 Bobcoins

Architecture and type contracts from the spec

Bob read docs/SPEC.md and the whole demo repository, then produced the module architecture, a mermaid data-flow diagram, the TypeScript contracts and a per-migration acceptance table.

Accepted. One claim in the acceptance table (LS002 on a NOT NULL column with a volatile default) was rejected in review and corrected.

docs/ARCHITECTURE.mdsrc/engine/types.ts
IBM Bob task 1 session summary
TASK 02Agent mode · todo list · test loop3.2 Bobcoins

Static engine: splitter, classifier, 12 rules, impact model, 53 tests

Bob implemented the SQL statement splitter (comments, strings, dollar-quoting), the multi-action ALTER TABLE classifier, rules LS001–LS012, the impact estimator and analyzeRepo, then iterated on vitest until green.

Accepted after independent re-run (53/53 tests, tsc clean). Review found LS002 still over-firing; sent back to Bob in task 3.

src/engine/split.tssrc/engine/classify.tssrc/engine/rules.tssrc/engine/impact.tssrc/engine/analyze.tssrc/engine/rules.test.ts
IBM Bob task 2 session summary
TASK 03Agent mode · parallel test runs · debugging2.94 Bobcoins

PostgreSQL lock probe in PGlite + fix of a reviewed defect

Bob implemented the empirical probe: replay every statement inside an embedded PostgreSQL, read pg_locks for the backend, detect table rewrites via relfilenode, seed synthetic rows, and handle CONCURRENTLY outside transactions. It also fixed the LS002 over-firing found in review of task 2.

Accepted after independent re-run: 58/58 tests, tsc clean; CLI now shows measured locks (e.g. CREATE INDEX → ShareLock; ALTER TYPE → AccessExclusiveLock + rewrite).

src/engine/probe.tssrc/engine/probe.test.tssrc/engine/analyze.tssrc/engine/rules.tssrc/engine/rules.test.ts
IBM Bob task 3 session summary
TASK 04Custom mode · mode rules · skill1.1 Bobcoins

Migration Surgeon custom mode, lock reference and lock-audit skill

Bob authored .bob/custom_modes.yaml (edit access restricted by fileRegex), a PostgreSQL lock reference it reads at rewrite time, the lock-audit skill checklist and a developer guide. It self-tested the fileRegex and tightened it so the mode cannot edit LockSmith's own engine.

Accepted. YAML validated independently; Bob IDE picked the mode up immediately (visible in the mode picker).

.bob/custom_modes.yaml.bob/rules-migration-surgeon/01-postgres-locks.md.bob/skills/lock-audit/SKILL.mddocs/BOB_MODE.md
IBM Bob task 4 session summary
TASK 05Custom mode · skill · 6 parallel subagents2.21 Bobcoins

Bob rewrites the release: 6 parallel subagents, gate FAIL → PASS

In Migration Surgeon mode Bob ran the lock-audit skill on a working copy of the demo repo: it ran the gate, spawned one subagent per flagged migration in parallel, replaced 6 dangerous migrations with 13 ordered safe files, updated app code for dual-writes, re-ran the gate, and withdrew its own column-drop step when it recognised the app still used the column.

Accepted after independent gate run: FAIL (risk 100) → PASS (risk 40). The remaining finding was an engine false positive, fixed in task 6.

demo-repo-safe/db/migrations/*demo-repo-safe/src/customers.tsdemo-repo-safe/src/orders.tsdemo-repo-safe/locksmith-summary.md
IBM Bob task 5 session summary
TASK 06Agent mode · debugging · review2.59 Bobcoins

Debugging an engine false positive + security review of the public API

Bob taught LS005 the PostgreSQL 12+ pattern (validated CHECK (col IS NOT NULL) makes SET NOT NULL scan-free) with regression tests, and reviewed /api/analyze, fixing four issues: unbounded body before JSON parse, unbounded tableStats loop, array bypass of the object guard, and internal error leakage.

Accepted after independent re-run: 61/61 tests, tsc clean; Bob-rewritten repo now scores risk 0 with 0 findings, original still FAIL/100.

src/engine/rules.tssrc/engine/rules.test.tssrc/app/api/analyze/route.ts
IBM Bob task 6 session summary