Keeping adopted packs current

Pull pack-definition changes into an already-adopted ctxgrd.toml without losing your hand edits.

Background

pack add writes a self-contained snapshot of the pack’s namespace blocks into ctxgrd.toml and stamps each block with a provenance comment:

# pack: [email protected] sha:a1b2c3d4

The version and content fingerprint let ctxgrd later detect two things:

  • Drift – the pack’s definition has evolved since your block was generated.
  • Hand edits – you have changed the block after generation (the on-disk bytes no longer match the fingerprint).

The older bare # pack: <name> form (written by binaries before 0.35.0) is still valid. An older binary treats the @version sha: suffix as an inert comment and keeps linting normally.

Typical workflow after a ctxgrd upgrade

1. Check for drift

ctxgrd pack outdated

Reports every block whose pack shape has evolved since it was generated. Read-only – nothing is written.

Exit codes:

  • 0 – all blocks are current.
  • 1 – one or more blocks have drifted.
  • 2 – config error (ctxgrd.toml unreadable or malformed).

2. Preview the migration

ctxgrd pack migrate --dry-run

Shows what the migration would do without touching ctxgrd.toml. For each provenance-stamped block ctxgrd determines:

  • Clean block – on-disk bytes match the fingerprint. The block would be rewritten to the pack’s current shape and re-stamped.
  • Dirty block – bytes differ from the fingerprint (you hand-edited it). The block would be left untouched; the diff between the on-disk block and the proposed shape is shown for you to reconcile.

For agent or CI use, request structured output:

ctxgrd pack migrate --dry-run --format json

3. Apply the migration

ctxgrd pack migrate

Rewrites every clean block in place and re-stamps its provenance. Dirty blocks are left untouched and their diffs are printed to stdout so you can reconcile them by hand (or hand the diff to an agent).

Running pack migrate a second time on an already-migrated config is a no-op.

Exit codes:

  • 0 – migration complete; no dirty blocks remain.
  • 1 – migration ran but one or more dirty blocks were left for manual resolution.
  • 2 – config error.

4. Resolve dirty-block diffs

For each dirty block the output shows:

  • On-disk – what is currently in ctxgrd.toml.
  • Proposed – what the pack’s current shape would write.

Open ctxgrd.toml and reconcile the two by hand. Common cases:

  • You added a rule the new shape also includes – keep it once.
  • You added a rule the new shape does not include – keep your addition, accept the structural changes around it.
  • You renamed or removed a namespace section – incorporate the structural rename from the proposed block and preserve your customizations.

After editing, run ctxgrd to confirm the config lints clean, then run ctxgrd pack outdated again – a dirty block you have fully reconciled still shows as dirty (the fingerprint reflects the original generated bytes, not your hand-edited version). That is expected: fingerprint-dirty blocks will always report as drift candidates. If you want the block to be tracked as clean going forward, delete it and re-add the pack namespace by hand or run pack add after removing the old block.

Machine-readable output

Both commands support --format json for use in CI or agent-driven fleet workflows:

ctxgrd pack outdated --format json
ctxgrd pack migrate --dry-run --format json

The exit-code contract (0 / 1 / 2) is stable across formats, so a script or agent can branch on outcome without parsing text.

What migration does not do

  • It does not remove namespace blocks whose paths glob matches no files on disk. That cleanup (e.g. a [GEMINI] block in a repo with no GEMINI.md) is a separate step not yet surfaced as a command.
  • It does not merge hand-edited lines into the new shape automatically. The diff is yours to resolve.
  • It does not push or commit anything. Migration rewrites ctxgrd.toml in place; committing the result is your step.