packages feed

seihou-cli-0.5.0.0: help/migrations.md

MIGRATIONS

Seihou has two migration systems. Deterministic module migrations use typed
filesystem operations and `seihou migrate`. Agent-guided blueprint migrations
use one library-upgrade prompt per version edge and `seihou agent migrate`.

BLUEPRINT MIGRATIONS

  A library blueprint declares entries with `from`, `to`, and a Markdown
  `prompt`. Consumers always supply explicit dotted numeric versions:

    seihou agent migrate my-library --from 1.0.0 --to 3.0.0

  The planner orders in-window entries by `from` and permits gaps. Each entry
  runs in its own provider session, then receives an exact-edge receipt before
  the next starts. A normal rerun resumes after receipts; --rerun repeats them.
  Parent --debug renders pending prompts in order without launching a provider
  or changing the manifest. Migration mode never applies blueprint baseModules.

  A receipt means the agent interaction completed successfully. It does not
  verify that Cabal, npm, Cargo, or another package manager reports the target.

DETERMINISTIC MODULE MIGRATIONS

A migration is an author-declared sequence of file-system operations that
moves a project's working tree from one module version to another. When a
module is upgraded — say, from haskell-base 1.0.0 to 2.0.0 — the project
that has it applied may need to rename directories, drop files that v2
doesn't ship, or run a one-off command. Migrations let module authors
ship that knowledge inside `module.dhall` so consumers don't have to read
a CHANGELOG and reconcile by hand.

WHEN TO AUTHOR A MIGRATION

  Add a migration when a new version of your module changes the *layout*
  of the files it generates: a directory rename, a file removal, a path
  pattern shift. You do not need a migration for content-only changes;
  `seihou update <target>` already reconciles newly generated content with
  the project. Use migrations specifically for layout changes that template
  regeneration cannot infer on its own.

THE MIGRATION RECORD

  Each migration is one record in the `migrations` field of `module.dhall`:

    , migrations =
        [ S.Migration::{ from = "1.0.0"
                       , to = "2.0.0"
                       , ops =
                           [ S.MigrationOp.MoveDir
                               { src = "app", dest = "src" }
                           , S.MigrationOp.DeleteFile
                               { path = "Setup.hs" }
                           ]
                       }
        ]

  - `from` and `to` are dotted versions parsed by `parseVersion`.
  - `ops` is the list of operations applied in order.

OPERATIONS

  Authors compose migrations from five typed operations:

  MoveFile    Rename a single tracked file. The engine rewrites the
              manifest's files-map key from src to dest so subsequent
              `seihou status` and `seihou diff` keep working.

                S.MigrationOp.MoveFile { src = "lib/Old.hs", dest = "src/New.hs" }

  MoveDir     Rename a directory. Every manifest entry whose path lies
              under src/ has its key rewritten with the dest/ prefix.

                S.MigrationOp.MoveDir { src = "app", dest = "src" }

  DeleteFile  Remove a tracked file. Drops the manifest entry.

                S.MigrationOp.DeleteFile { path = "Setup.hs" }

  DeleteDir   Recursively remove a directory and drop every manifest
              entry under that prefix.

                S.MigrationOp.DeleteDir { path = "obsolete" }

  RunCommand  Run a shell command. The escape hatch for changes the
              other ops can't express. The manifest is *not*
              automatically updated; if your command moves files, you
              are responsible for following it with explicit
              MoveFile/DeleteFile ops or living with manifest drift.

                S.MigrationOp.RunCommand
                  { run = "cabal run my-tool -- migrate", workDir = None Text }

WINDOW SEMANTICS

  When a user runs `seihou migrate`, the planner walks every declared
  migration whose [from, to] range falls inside [installed, target] in
  ascending `from` order, advancing a cursor as it goes. Migrations
  whose `from` is already past the cursor (overlap or stale) are
  skipped silently; migrations whose `to` exceeds the target are
  skipped (a future invocation with a higher target will pick them
  up).

  The cursor never has to land on a declared `from`. Gaps are
  permitted:

    installed: 0.2     target: 0.6
    declared:  0.2 → 0.3,   0.5 → 0.6

  Both edges run in order. The 0.3 → 0.5 gap has no declared
  migration and the planner does not stop — it skips the gap and
  keeps walking.

MANIFEST ALWAYS LANDS AT TARGET

  After a successful (non-dry-run) migration, the manifest's recorded
  version for the module advances to the supplied target — regardless
  of whether any of the declared migrations bridged every gap. A plan
  with zero in-window migrations is a "pure version bump" that
  advances the manifest's `moduleVersion` field without running any
  ops. The terms "blocked migration", "benign upgrade", and
  "bump-through" are gone; every invocation either has nothing to do
  (`installed == target`) or lands the manifest at the target.

DUPLICATES AND OVERLAPS

  If two migrations share the same `from`, that is an ambiguity and
  the planner refuses (`MigrationDuplicateEdge`). Authors who want a
  "leapfrog" migration alongside the smaller steps should declare the
  longer span and let the smaller overlapping edges sit unused — the
  cursor will advance past them after picking the leapfrog.

CONFLICT SEMANTICS

  Mirroring `seihou remove`, the engine classifies each
  file-targeted op into one of three states before touching disk:

    MFSafe     Disk hash matches the manifest's recorded hash. Free
               to move or delete.
    MFConflict Disk hash differs. The user has edited the file since
               it was generated. The engine refuses to overwrite
               unless `--force` is passed.
    MFGone     File is absent on disk. The op is a no-op.

  The conflict check happens up front. With one or more `MFConflict`
  paths and no `--force`, no disk mutation occurs. With `--force`, the
  migration proceeds and the user-edited bytes ride along (a
  MoveFile preserves content; a DeleteFile drops it).

DRY RUN

  `seihou migrate <module> --dry-run` prints the plan and exits. The
  output is identical to a real run, minus the actual disk
  modifications. Use this before any non-trivial migration to see
  what would be touched.

  For a machine-readable form, pass `--json` (works alongside
  `--dry-run`).

PROJECT UPDATE INTEGRATION

  Routine project updates use `seihou update`. It fetches candidate sources
  into a staging area, plans applicable migrations automatically, and then
  reconciles the newly generated content with user edits. The migration and
  template transitions therefore share one preview, confirmation, recovery,
  and manifest-publication boundary.

  Use `seihou migrate <module>` directly for focused recovery or when you
  deliberately need its lower-level controls, such as an intermediate
  `--to VERSION`. See `seihou help update` for the routine workflow.

CACHE UPGRADE INTEGRATION

  `seihou upgrade` updates the central installed copy under
  ~/.config/seihou/installed/<name>/ but does *not* rewrite project
  trees by default. After an upgrade that leaves a recorded project behind,
  `seihou upgrade` points the user at `seihou update` so migrations and
  generated content advance together:

    note: haskell-base has 1 migration(s) pending (1.0.0 → 2.0.0);
          run 'seihou update' to reconcile the recorded project application

  `--with-migrations` remains available for compatibility when only the
  lower-level migration should run after refreshing the cache.

STATUS INTEGRATION

  `seihou status` shows a `Pending migration: <from> -> <to> (<N>
  step(s)). Run: seihou update <target>` sub-line under any applied
  module whose installed copy has advanced past the manifest's
  recorded version. `<N>` is the count of in-window declared
  migrations that will run; it may be zero (the version range has no
  applicable migrations and the run will only advance the manifest).
  Recommendations are deduplicated by recorded top-level application.

MANIFEST GUARANTEE

  After a successful (non-dry-run) migration, the manifest's files map
  reflects the new paths exactly: a MoveFile rewrites one key, a
  MoveDir rewrites every contained key, deletes drop their entries,
  and the manifest's `genAt` is bumped. The named applied module's
  `moduleVersion` is updated to the supplied target (which may differ
  from the highest `to` in the applied migrations — see "Manifest
  always lands at target" above). `seihou diff` after a clean
  migration is empty; `seihou status` shows no pending migrations.

SEE ALSO

  seihou update --help
  seihou help update
  seihou migrate --help
  seihou upgrade --help
  docs/user/migrations.md